pptx-glimpse 0.2.1 → 0.3.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/README.md +4 -2
- package/dist/index.cjs +351 -96
- package/dist/index.d.cts +98 -1
- package/dist/index.d.ts +98 -1
- package/dist/index.js +349 -96
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/pptx-glimpse)
|
|
4
4
|
|
|
5
|
-
A
|
|
5
|
+
A lightweight JavaScript library for rendering PowerPoint (.pptx) files as SVG or PNG in Node.js. No LibreOffice required.
|
|
6
6
|
|
|
7
|
-
[Demo](https://pptx-glimpse.vercel.app/) | [npm](https://www.npmjs.com/package/pptx-glimpse)
|
|
7
|
+
**[Try the Demo](https://pptx-glimpse.vercel.app/)** | [npm](https://www.npmjs.com/package/pptx-glimpse)
|
|
8
|
+
|
|
9
|
+

|
|
8
10
|
|
|
9
11
|
## Motivation
|
|
10
12
|
|
package/dist/index.cjs
CHANGED
|
@@ -35,6 +35,8 @@ __export(index_exports, {
|
|
|
35
35
|
convertPptxToPng: () => convertPptxToPng,
|
|
36
36
|
convertPptxToSvg: () => convertPptxToSvg,
|
|
37
37
|
createFontMapping: () => createFontMapping,
|
|
38
|
+
createOpentypeSetupFromBuffers: () => createOpentypeSetupFromBuffers,
|
|
39
|
+
createOpentypeTextMeasurerFromBuffers: () => createOpentypeTextMeasurerFromBuffers,
|
|
38
40
|
getMappedFont: () => getMappedFont,
|
|
39
41
|
getWarningEntries: () => getWarningEntries,
|
|
40
42
|
getWarningSummary: () => getWarningSummary
|
|
@@ -3813,6 +3815,65 @@ function renderPlaceholder(mimeType, w, h, transformAttr) {
|
|
|
3813
3815
|
].join("");
|
|
3814
3816
|
}
|
|
3815
3817
|
|
|
3818
|
+
// src/warning-logger.ts
|
|
3819
|
+
var PREFIX = "[pptx-glimpse]";
|
|
3820
|
+
var currentLevel = "off";
|
|
3821
|
+
var entries = [];
|
|
3822
|
+
var featureCounts = /* @__PURE__ */ new Map();
|
|
3823
|
+
function initWarningLogger(level) {
|
|
3824
|
+
currentLevel = level;
|
|
3825
|
+
entries = [];
|
|
3826
|
+
featureCounts.clear();
|
|
3827
|
+
}
|
|
3828
|
+
function warn(feature, message, context) {
|
|
3829
|
+
if (currentLevel === "off") return;
|
|
3830
|
+
entries.push({ feature, message, ...context !== void 0 && { context } });
|
|
3831
|
+
const existing = featureCounts.get(feature);
|
|
3832
|
+
if (existing) {
|
|
3833
|
+
existing.count++;
|
|
3834
|
+
} else {
|
|
3835
|
+
featureCounts.set(feature, { message, count: 1 });
|
|
3836
|
+
}
|
|
3837
|
+
if (currentLevel === "debug") {
|
|
3838
|
+
const ctx = context ? ` (${context})` : "";
|
|
3839
|
+
console.warn(`${PREFIX} SKIP: ${feature} - ${message}${ctx}`);
|
|
3840
|
+
}
|
|
3841
|
+
}
|
|
3842
|
+
function debug(feature, message, context) {
|
|
3843
|
+
if (currentLevel !== "debug") return;
|
|
3844
|
+
entries.push({ feature, message, ...context !== void 0 && { context } });
|
|
3845
|
+
const existing = featureCounts.get(feature);
|
|
3846
|
+
if (existing) {
|
|
3847
|
+
existing.count++;
|
|
3848
|
+
} else {
|
|
3849
|
+
featureCounts.set(feature, { message, count: 1 });
|
|
3850
|
+
}
|
|
3851
|
+
const ctx = context ? ` (${context})` : "";
|
|
3852
|
+
console.warn(`${PREFIX} DEBUG: ${feature} - ${message}${ctx}`);
|
|
3853
|
+
}
|
|
3854
|
+
function getWarningSummary() {
|
|
3855
|
+
const features = [];
|
|
3856
|
+
for (const [feature, { message, count }] of featureCounts) {
|
|
3857
|
+
features.push({ feature, message, count });
|
|
3858
|
+
}
|
|
3859
|
+
return { totalCount: entries.length, features };
|
|
3860
|
+
}
|
|
3861
|
+
function flushWarnings() {
|
|
3862
|
+
const summary = getWarningSummary();
|
|
3863
|
+
if (currentLevel !== "off" && summary.features.length > 0) {
|
|
3864
|
+
console.warn(`${PREFIX} Summary: ${summary.features.length} unsupported feature(s) detected`);
|
|
3865
|
+
for (const { feature, count } of summary.features) {
|
|
3866
|
+
console.warn(` - ${feature}: ${count} occurrence(s)`);
|
|
3867
|
+
}
|
|
3868
|
+
}
|
|
3869
|
+
entries = [];
|
|
3870
|
+
featureCounts.clear();
|
|
3871
|
+
return summary;
|
|
3872
|
+
}
|
|
3873
|
+
function getWarningEntries() {
|
|
3874
|
+
return entries;
|
|
3875
|
+
}
|
|
3876
|
+
|
|
3816
3877
|
// src/renderer/chart-renderer.ts
|
|
3817
3878
|
var DEFAULT_SERIES_COLORS = [
|
|
3818
3879
|
{ hex: "#4472C4", alpha: 1 },
|
|
@@ -3894,11 +3955,20 @@ function renderChartTitle(title, chartWidth) {
|
|
|
3894
3955
|
function renderBarChart(chart, x, y, w, h) {
|
|
3895
3956
|
const parts = [];
|
|
3896
3957
|
const { series, categories } = chart;
|
|
3897
|
-
if (series.length === 0)
|
|
3958
|
+
if (series.length === 0) {
|
|
3959
|
+
debug("chart.bar", "series is empty");
|
|
3960
|
+
return "";
|
|
3961
|
+
}
|
|
3898
3962
|
const maxVal = getMaxValue(series);
|
|
3899
|
-
if (maxVal === 0)
|
|
3963
|
+
if (maxVal === 0) {
|
|
3964
|
+
debug("chart.bar", "max value is 0");
|
|
3965
|
+
return "";
|
|
3966
|
+
}
|
|
3900
3967
|
const catCount = categories.length || Math.max(...series.map((s) => s.values.length));
|
|
3901
|
-
if (catCount === 0)
|
|
3968
|
+
if (catCount === 0) {
|
|
3969
|
+
debug("chart.bar", "category count is 0");
|
|
3970
|
+
return "";
|
|
3971
|
+
}
|
|
3902
3972
|
const isHorizontal = chart.barDirection === "bar";
|
|
3903
3973
|
parts.push(
|
|
3904
3974
|
`<line x1="${round3(x)}" y1="${round3(y + h)}" x2="${round3(x + w)}" y2="${round3(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
@@ -3958,11 +4028,20 @@ function renderBarChart(chart, x, y, w, h) {
|
|
|
3958
4028
|
function renderLineChart(chart, x, y, w, h) {
|
|
3959
4029
|
const parts = [];
|
|
3960
4030
|
const { series, categories } = chart;
|
|
3961
|
-
if (series.length === 0)
|
|
4031
|
+
if (series.length === 0) {
|
|
4032
|
+
debug("chart.line", "series is empty");
|
|
4033
|
+
return "";
|
|
4034
|
+
}
|
|
3962
4035
|
const maxVal = getMaxValue(series);
|
|
3963
|
-
if (maxVal === 0)
|
|
4036
|
+
if (maxVal === 0) {
|
|
4037
|
+
debug("chart.line", "max value is 0");
|
|
4038
|
+
return "";
|
|
4039
|
+
}
|
|
3964
4040
|
const catCount = categories.length || Math.max(...series.map((s) => s.values.length));
|
|
3965
|
-
if (catCount === 0)
|
|
4041
|
+
if (catCount === 0) {
|
|
4042
|
+
debug("chart.line", "category count is 0");
|
|
4043
|
+
return "";
|
|
4044
|
+
}
|
|
3966
4045
|
parts.push(
|
|
3967
4046
|
`<line x1="${round3(x)}" y1="${round3(y + h)}" x2="${round3(x + w)}" y2="${round3(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
3968
4047
|
);
|
|
@@ -3998,11 +4077,20 @@ function renderLineChart(chart, x, y, w, h) {
|
|
|
3998
4077
|
function renderAreaChart(chart, x, y, w, h) {
|
|
3999
4078
|
const parts = [];
|
|
4000
4079
|
const { series, categories } = chart;
|
|
4001
|
-
if (series.length === 0)
|
|
4080
|
+
if (series.length === 0) {
|
|
4081
|
+
debug("chart.area", "series is empty");
|
|
4082
|
+
return "";
|
|
4083
|
+
}
|
|
4002
4084
|
const maxVal = getMaxValue(series);
|
|
4003
|
-
if (maxVal === 0)
|
|
4085
|
+
if (maxVal === 0) {
|
|
4086
|
+
debug("chart.area", "max value is 0");
|
|
4087
|
+
return "";
|
|
4088
|
+
}
|
|
4004
4089
|
const catCount = categories.length || Math.max(...series.map((s) => s.values.length));
|
|
4005
|
-
if (catCount === 0)
|
|
4090
|
+
if (catCount === 0) {
|
|
4091
|
+
debug("chart.area", "category count is 0");
|
|
4092
|
+
return "";
|
|
4093
|
+
}
|
|
4006
4094
|
parts.push(
|
|
4007
4095
|
`<line x1="${round3(x)}" y1="${round3(y + h)}" x2="${round3(x + w)}" y2="${round3(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
4008
4096
|
);
|
|
@@ -4039,9 +4127,15 @@ function renderAreaChart(chart, x, y, w, h) {
|
|
|
4039
4127
|
function renderPieChart(chart, x, y, w, h) {
|
|
4040
4128
|
const parts = [];
|
|
4041
4129
|
const series = chart.series[0];
|
|
4042
|
-
if (!series || series.values.length === 0)
|
|
4130
|
+
if (!series || series.values.length === 0) {
|
|
4131
|
+
debug("chart.pie", "series is empty or has no values");
|
|
4132
|
+
return "";
|
|
4133
|
+
}
|
|
4043
4134
|
const total = series.values.reduce((sum, v) => sum + v, 0);
|
|
4044
|
-
if (total === 0)
|
|
4135
|
+
if (total === 0) {
|
|
4136
|
+
debug("chart.pie", "total value is 0");
|
|
4137
|
+
return "";
|
|
4138
|
+
}
|
|
4045
4139
|
const cx = x + w / 2;
|
|
4046
4140
|
const cy = y + h / 2;
|
|
4047
4141
|
const r = Math.min(w, h) / 2 * 0.85;
|
|
@@ -4071,9 +4165,15 @@ function renderPieChart(chart, x, y, w, h) {
|
|
|
4071
4165
|
function renderDoughnutChart(chart, x, y, w, h) {
|
|
4072
4166
|
const parts = [];
|
|
4073
4167
|
const series = chart.series[0];
|
|
4074
|
-
if (!series || series.values.length === 0)
|
|
4168
|
+
if (!series || series.values.length === 0) {
|
|
4169
|
+
debug("chart.doughnut", "series is empty or has no values");
|
|
4170
|
+
return "";
|
|
4171
|
+
}
|
|
4075
4172
|
const total = series.values.reduce((sum, v) => sum + v, 0);
|
|
4076
|
-
if (total === 0)
|
|
4173
|
+
if (total === 0) {
|
|
4174
|
+
debug("chart.doughnut", "total value is 0");
|
|
4175
|
+
return "";
|
|
4176
|
+
}
|
|
4077
4177
|
const cx = x + w / 2;
|
|
4078
4178
|
const cy = y + h / 2;
|
|
4079
4179
|
const outerR = Math.min(w, h) / 2 * 0.85;
|
|
@@ -4112,7 +4212,10 @@ function renderDoughnutChart(chart, x, y, w, h) {
|
|
|
4112
4212
|
function renderScatterChart(chart, x, y, w, h) {
|
|
4113
4213
|
const parts = [];
|
|
4114
4214
|
const { series } = chart;
|
|
4115
|
-
if (series.length === 0)
|
|
4215
|
+
if (series.length === 0) {
|
|
4216
|
+
debug("chart.scatter", "series is empty");
|
|
4217
|
+
return "";
|
|
4218
|
+
}
|
|
4116
4219
|
let maxX = 0;
|
|
4117
4220
|
let maxY = 0;
|
|
4118
4221
|
for (const s of series) {
|
|
@@ -4144,7 +4247,10 @@ function renderScatterChart(chart, x, y, w, h) {
|
|
|
4144
4247
|
function renderBubbleChart(chart, x, y, w, h) {
|
|
4145
4248
|
const parts = [];
|
|
4146
4249
|
const { series } = chart;
|
|
4147
|
-
if (series.length === 0)
|
|
4250
|
+
if (series.length === 0) {
|
|
4251
|
+
debug("chart.bubble", "series is empty");
|
|
4252
|
+
return "";
|
|
4253
|
+
}
|
|
4148
4254
|
let maxX = 0;
|
|
4149
4255
|
let maxY = 0;
|
|
4150
4256
|
let maxBubble = 0;
|
|
@@ -4186,11 +4292,20 @@ function renderBubbleChart(chart, x, y, w, h) {
|
|
|
4186
4292
|
function renderRadarChart(chart, x, y, w, h) {
|
|
4187
4293
|
const parts = [];
|
|
4188
4294
|
const { series, categories } = chart;
|
|
4189
|
-
if (series.length === 0)
|
|
4295
|
+
if (series.length === 0) {
|
|
4296
|
+
debug("chart.radar", "series is empty");
|
|
4297
|
+
return "";
|
|
4298
|
+
}
|
|
4190
4299
|
const maxVal = getMaxValue(series);
|
|
4191
|
-
if (maxVal === 0)
|
|
4300
|
+
if (maxVal === 0) {
|
|
4301
|
+
debug("chart.radar", "max value is 0");
|
|
4302
|
+
return "";
|
|
4303
|
+
}
|
|
4192
4304
|
const catCount = categories.length || Math.max(...series.map((s) => s.values.length));
|
|
4193
|
-
if (catCount === 0)
|
|
4305
|
+
if (catCount === 0) {
|
|
4306
|
+
debug("chart.radar", "category count is 0");
|
|
4307
|
+
return "";
|
|
4308
|
+
}
|
|
4194
4309
|
const cx = x + w / 2;
|
|
4195
4310
|
const cy = y + h / 2;
|
|
4196
4311
|
const radius = Math.min(w, h) / 2 * 0.85;
|
|
@@ -4254,12 +4369,18 @@ function renderRadarChart(chart, x, y, w, h) {
|
|
|
4254
4369
|
function renderStockChart(chart, x, y, w, h) {
|
|
4255
4370
|
const parts = [];
|
|
4256
4371
|
const { series, categories } = chart;
|
|
4257
|
-
if (series.length < 3)
|
|
4372
|
+
if (series.length < 3) {
|
|
4373
|
+
debug("chart.stock", `insufficient series count: ${series.length} (need at least 3)`);
|
|
4374
|
+
return "";
|
|
4375
|
+
}
|
|
4258
4376
|
const highSeries = series[0];
|
|
4259
4377
|
const lowSeries = series[1];
|
|
4260
4378
|
const closeSeries = series[2];
|
|
4261
4379
|
const catCount = categories.length || highSeries.values.length;
|
|
4262
|
-
if (catCount === 0)
|
|
4380
|
+
if (catCount === 0) {
|
|
4381
|
+
debug("chart.stock", "category count is 0");
|
|
4382
|
+
return "";
|
|
4383
|
+
}
|
|
4263
4384
|
let maxVal = 0;
|
|
4264
4385
|
let minVal = Infinity;
|
|
4265
4386
|
for (const s of [highSeries, lowSeries, closeSeries]) {
|
|
@@ -4268,7 +4389,10 @@ function renderStockChart(chart, x, y, w, h) {
|
|
|
4268
4389
|
minVal = Math.min(minVal, v);
|
|
4269
4390
|
}
|
|
4270
4391
|
}
|
|
4271
|
-
if (maxVal === minVal)
|
|
4392
|
+
if (maxVal === minVal) {
|
|
4393
|
+
debug("chart.stock", "max equals min value");
|
|
4394
|
+
return "";
|
|
4395
|
+
}
|
|
4272
4396
|
parts.push(
|
|
4273
4397
|
`<line x1="${round3(x)}" y1="${round3(y + h)}" x2="${round3(x + w)}" y2="${round3(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
4274
4398
|
);
|
|
@@ -4304,10 +4428,16 @@ function renderStockChart(chart, x, y, w, h) {
|
|
|
4304
4428
|
function renderSurfaceChart(chart, x, y, w, h) {
|
|
4305
4429
|
const parts = [];
|
|
4306
4430
|
const { series, categories } = chart;
|
|
4307
|
-
if (series.length === 0)
|
|
4431
|
+
if (series.length === 0) {
|
|
4432
|
+
debug("chart.surface", "series is empty");
|
|
4433
|
+
return "";
|
|
4434
|
+
}
|
|
4308
4435
|
const rows = series.length;
|
|
4309
4436
|
const cols = categories.length || Math.max(...series.map((s) => s.values.length));
|
|
4310
|
-
if (cols === 0)
|
|
4437
|
+
if (cols === 0) {
|
|
4438
|
+
debug("chart.surface", "column count is 0");
|
|
4439
|
+
return "";
|
|
4440
|
+
}
|
|
4311
4441
|
let minVal = Infinity;
|
|
4312
4442
|
let maxVal = -Infinity;
|
|
4313
4443
|
for (const s of series) {
|
|
@@ -4380,9 +4510,15 @@ function heatmapColor(t) {
|
|
|
4380
4510
|
function renderOfPieChart(chart, x, y, w, h) {
|
|
4381
4511
|
const parts = [];
|
|
4382
4512
|
const series = chart.series[0];
|
|
4383
|
-
if (!series || series.values.length === 0)
|
|
4513
|
+
if (!series || series.values.length === 0) {
|
|
4514
|
+
debug("chart.ofPie", "series is empty or has no values");
|
|
4515
|
+
return "";
|
|
4516
|
+
}
|
|
4384
4517
|
const total = series.values.reduce((sum, v) => sum + v, 0);
|
|
4385
|
-
if (total === 0)
|
|
4518
|
+
if (total === 0) {
|
|
4519
|
+
debug("chart.ofPie", "total value is 0");
|
|
4520
|
+
return "";
|
|
4521
|
+
}
|
|
4386
4522
|
const splitPos = chart.splitPos ?? 2;
|
|
4387
4523
|
const secondPieSize = chart.secondPieSize ?? 75;
|
|
4388
4524
|
const isBarOfPie = chart.ofPieType === "bar";
|
|
@@ -4747,65 +4883,6 @@ async function svgToPng(svgString, options) {
|
|
|
4747
4883
|
return { png: result.data, width: result.info.width, height: result.info.height };
|
|
4748
4884
|
}
|
|
4749
4885
|
|
|
4750
|
-
// src/warning-logger.ts
|
|
4751
|
-
var PREFIX = "[pptx-glimpse]";
|
|
4752
|
-
var currentLevel = "off";
|
|
4753
|
-
var entries = [];
|
|
4754
|
-
var featureCounts = /* @__PURE__ */ new Map();
|
|
4755
|
-
function initWarningLogger(level) {
|
|
4756
|
-
currentLevel = level;
|
|
4757
|
-
entries = [];
|
|
4758
|
-
featureCounts.clear();
|
|
4759
|
-
}
|
|
4760
|
-
function warn(feature, message, context) {
|
|
4761
|
-
if (currentLevel === "off") return;
|
|
4762
|
-
entries.push({ feature, message, ...context !== void 0 && { context } });
|
|
4763
|
-
const existing = featureCounts.get(feature);
|
|
4764
|
-
if (existing) {
|
|
4765
|
-
existing.count++;
|
|
4766
|
-
} else {
|
|
4767
|
-
featureCounts.set(feature, { message, count: 1 });
|
|
4768
|
-
}
|
|
4769
|
-
if (currentLevel === "debug") {
|
|
4770
|
-
const ctx = context ? ` (${context})` : "";
|
|
4771
|
-
console.warn(`${PREFIX} SKIP: ${feature} - ${message}${ctx}`);
|
|
4772
|
-
}
|
|
4773
|
-
}
|
|
4774
|
-
function debug(feature, message, context) {
|
|
4775
|
-
if (currentLevel !== "debug") return;
|
|
4776
|
-
entries.push({ feature, message, ...context !== void 0 && { context } });
|
|
4777
|
-
const existing = featureCounts.get(feature);
|
|
4778
|
-
if (existing) {
|
|
4779
|
-
existing.count++;
|
|
4780
|
-
} else {
|
|
4781
|
-
featureCounts.set(feature, { message, count: 1 });
|
|
4782
|
-
}
|
|
4783
|
-
const ctx = context ? ` (${context})` : "";
|
|
4784
|
-
console.warn(`${PREFIX} DEBUG: ${feature} - ${message}${ctx}`);
|
|
4785
|
-
}
|
|
4786
|
-
function getWarningSummary() {
|
|
4787
|
-
const features = [];
|
|
4788
|
-
for (const [feature, { message, count }] of featureCounts) {
|
|
4789
|
-
features.push({ feature, message, count });
|
|
4790
|
-
}
|
|
4791
|
-
return { totalCount: entries.length, features };
|
|
4792
|
-
}
|
|
4793
|
-
function flushWarnings() {
|
|
4794
|
-
const summary = getWarningSummary();
|
|
4795
|
-
if (currentLevel !== "off" && summary.features.length > 0) {
|
|
4796
|
-
console.warn(`${PREFIX} Summary: ${summary.features.length} unsupported feature(s) detected`);
|
|
4797
|
-
for (const { feature, count } of summary.features) {
|
|
4798
|
-
console.warn(` - ${feature}: ${count} occurrence(s)`);
|
|
4799
|
-
}
|
|
4800
|
-
}
|
|
4801
|
-
entries = [];
|
|
4802
|
-
featureCounts.clear();
|
|
4803
|
-
return summary;
|
|
4804
|
-
}
|
|
4805
|
-
function getWarningEntries() {
|
|
4806
|
-
return entries;
|
|
4807
|
-
}
|
|
4808
|
-
|
|
4809
4886
|
// src/parser/pptx-reader.ts
|
|
4810
4887
|
var import_fflate = require("fflate");
|
|
4811
4888
|
function readPptx(input) {
|
|
@@ -6183,10 +6260,16 @@ function parseCustomGeometry(custGeom) {
|
|
|
6183
6260
|
for (const path of paths) {
|
|
6184
6261
|
const w = Number(path["@_w"] ?? 0);
|
|
6185
6262
|
const h = Number(path["@_h"] ?? 0);
|
|
6186
|
-
if (w === 0 && h === 0)
|
|
6263
|
+
if (w === 0 && h === 0) {
|
|
6264
|
+
debug("custGeom.path", "path skipped: width and height are both 0");
|
|
6265
|
+
continue;
|
|
6266
|
+
}
|
|
6187
6267
|
const vars = evaluateGuides(avGd, gdGd, w, h);
|
|
6188
6268
|
const commands = buildPathCommands(path, vars);
|
|
6189
|
-
if (!commands)
|
|
6269
|
+
if (!commands) {
|
|
6270
|
+
debug("custGeom.path", "path skipped: failed to build path commands");
|
|
6271
|
+
continue;
|
|
6272
|
+
}
|
|
6190
6273
|
result.push({ width: w, height: h, commands });
|
|
6191
6274
|
}
|
|
6192
6275
|
return result.length > 0 ? result : null;
|
|
@@ -6759,7 +6842,10 @@ function parseImage(pic, rels, slidePath, archive, colorResolver) {
|
|
|
6759
6842
|
if (!rel) return null;
|
|
6760
6843
|
const mediaPath = resolveRelationshipTarget(slidePath, rel.target);
|
|
6761
6844
|
const mediaData = archive.media.get(mediaPath);
|
|
6762
|
-
if (!mediaData)
|
|
6845
|
+
if (!mediaData) {
|
|
6846
|
+
debug("picture.media", `media file not found: ${mediaPath}`);
|
|
6847
|
+
return null;
|
|
6848
|
+
}
|
|
6763
6849
|
const ext = mediaPath.split(".").pop()?.toLowerCase() ?? "png";
|
|
6764
6850
|
const mimeMap = {
|
|
6765
6851
|
png: "image/png",
|
|
@@ -6958,9 +7044,15 @@ function parseSmartArt(graphicData, transform, rels, slidePath, archive, colorRe
|
|
|
6958
7044
|
}
|
|
6959
7045
|
}
|
|
6960
7046
|
}
|
|
6961
|
-
if (!drawingPath)
|
|
7047
|
+
if (!drawingPath) {
|
|
7048
|
+
debug("smartArt.drawing", "diagramDrawing relationship not found");
|
|
7049
|
+
return null;
|
|
7050
|
+
}
|
|
6962
7051
|
const drawingXml = archive.files.get(drawingPath);
|
|
6963
|
-
if (!drawingXml)
|
|
7052
|
+
if (!drawingXml) {
|
|
7053
|
+
debug("smartArt.drawing", `drawing XML not found in archive: ${drawingPath}`);
|
|
7054
|
+
return null;
|
|
7055
|
+
}
|
|
6964
7056
|
const parsed = parseXml(drawingXml);
|
|
6965
7057
|
const drawing = parsed.drawing;
|
|
6966
7058
|
const spTree = drawing?.spTree;
|
|
@@ -8020,6 +8112,108 @@ function collectFontFilePaths(additionalDirs) {
|
|
|
8020
8112
|
return result;
|
|
8021
8113
|
}
|
|
8022
8114
|
|
|
8115
|
+
// src/font/ttc-parser.ts
|
|
8116
|
+
var TTC_TAG = 1953784678;
|
|
8117
|
+
function isTtcBuffer(data) {
|
|
8118
|
+
const view = toDataView(data);
|
|
8119
|
+
if (view.byteLength < 4) return false;
|
|
8120
|
+
return view.getUint32(0) === TTC_TAG;
|
|
8121
|
+
}
|
|
8122
|
+
function extractTtcFonts(data) {
|
|
8123
|
+
const view = toDataView(data);
|
|
8124
|
+
const bytes = toUint8Array(data);
|
|
8125
|
+
if (view.byteLength < 12) return [];
|
|
8126
|
+
if (view.getUint32(0) !== TTC_TAG) return [];
|
|
8127
|
+
const numFonts = view.getUint32(8);
|
|
8128
|
+
if (numFonts === 0) return [];
|
|
8129
|
+
const headerEnd = 12 + numFonts * 4;
|
|
8130
|
+
if (view.byteLength < headerEnd) return [];
|
|
8131
|
+
const results = [];
|
|
8132
|
+
for (let i = 0; i < numFonts; i++) {
|
|
8133
|
+
try {
|
|
8134
|
+
const fontOffset = view.getUint32(12 + i * 4);
|
|
8135
|
+
const extracted = extractSingleFont(view, bytes, fontOffset);
|
|
8136
|
+
if (extracted) results.push(extracted);
|
|
8137
|
+
} catch {
|
|
8138
|
+
}
|
|
8139
|
+
}
|
|
8140
|
+
return results;
|
|
8141
|
+
}
|
|
8142
|
+
function extractSingleFont(view, bytes, fontOffset) {
|
|
8143
|
+
if (fontOffset + 12 > view.byteLength) return null;
|
|
8144
|
+
const sfVersion = view.getUint32(fontOffset);
|
|
8145
|
+
const numTables = view.getUint16(fontOffset + 4);
|
|
8146
|
+
if (numTables === 0) return null;
|
|
8147
|
+
const tableRecordsStart = fontOffset + 12;
|
|
8148
|
+
const tableRecordsEnd = tableRecordsStart + numTables * 16;
|
|
8149
|
+
if (tableRecordsEnd > view.byteLength) return null;
|
|
8150
|
+
const tables = [];
|
|
8151
|
+
for (let i = 0; i < numTables; i++) {
|
|
8152
|
+
const recOffset = tableRecordsStart + i * 16;
|
|
8153
|
+
const tableOffset = view.getUint32(recOffset + 8);
|
|
8154
|
+
const tableLength = view.getUint32(recOffset + 12);
|
|
8155
|
+
if (tableOffset > view.byteLength || tableLength > view.byteLength - tableOffset) {
|
|
8156
|
+
return null;
|
|
8157
|
+
}
|
|
8158
|
+
tables.push({
|
|
8159
|
+
tag: view.getUint32(recOffset),
|
|
8160
|
+
checkSum: view.getUint32(recOffset + 4),
|
|
8161
|
+
offset: tableOffset,
|
|
8162
|
+
length: tableLength
|
|
8163
|
+
});
|
|
8164
|
+
}
|
|
8165
|
+
const headerSize = 12 + numTables * 16;
|
|
8166
|
+
let dataSize = 0;
|
|
8167
|
+
for (const table of tables) {
|
|
8168
|
+
dataSize += alignTo4(table.length);
|
|
8169
|
+
}
|
|
8170
|
+
const totalSize = headerSize + dataSize;
|
|
8171
|
+
const output = new ArrayBuffer(totalSize);
|
|
8172
|
+
const outView = new DataView(output);
|
|
8173
|
+
const outBytes = new Uint8Array(output);
|
|
8174
|
+
outView.setUint32(0, sfVersion);
|
|
8175
|
+
outView.setUint16(4, numTables);
|
|
8176
|
+
const { searchRange, entrySelector, rangeShift } = calcOffsetTableFields(numTables);
|
|
8177
|
+
outView.setUint16(6, searchRange);
|
|
8178
|
+
outView.setUint16(8, entrySelector);
|
|
8179
|
+
outView.setUint16(10, rangeShift);
|
|
8180
|
+
let currentDataOffset = headerSize;
|
|
8181
|
+
for (let i = 0; i < numTables; i++) {
|
|
8182
|
+
const table = tables[i];
|
|
8183
|
+
const recOffset = 12 + i * 16;
|
|
8184
|
+
outView.setUint32(recOffset, table.tag);
|
|
8185
|
+
outView.setUint32(recOffset + 4, table.checkSum);
|
|
8186
|
+
outView.setUint32(recOffset + 8, currentDataOffset);
|
|
8187
|
+
outView.setUint32(recOffset + 12, table.length);
|
|
8188
|
+
outBytes.set(bytes.subarray(table.offset, table.offset + table.length), currentDataOffset);
|
|
8189
|
+
currentDataOffset += alignTo4(table.length);
|
|
8190
|
+
}
|
|
8191
|
+
return output;
|
|
8192
|
+
}
|
|
8193
|
+
function alignTo4(n) {
|
|
8194
|
+
return n + 3 & ~3;
|
|
8195
|
+
}
|
|
8196
|
+
function calcOffsetTableFields(numTables) {
|
|
8197
|
+
let searchRange = 16;
|
|
8198
|
+
let entrySelector = 0;
|
|
8199
|
+
while (searchRange * 2 <= numTables * 16) {
|
|
8200
|
+
searchRange *= 2;
|
|
8201
|
+
entrySelector++;
|
|
8202
|
+
}
|
|
8203
|
+
const rangeShift = numTables * 16 - searchRange;
|
|
8204
|
+
return { searchRange, entrySelector, rangeShift };
|
|
8205
|
+
}
|
|
8206
|
+
function toDataView(data) {
|
|
8207
|
+
if (data instanceof Uint8Array) {
|
|
8208
|
+
return new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
8209
|
+
}
|
|
8210
|
+
return new DataView(data);
|
|
8211
|
+
}
|
|
8212
|
+
function toUint8Array(data) {
|
|
8213
|
+
if (data instanceof Uint8Array) return data;
|
|
8214
|
+
return new Uint8Array(data);
|
|
8215
|
+
}
|
|
8216
|
+
|
|
8023
8217
|
// src/font/opentype-helpers.ts
|
|
8024
8218
|
async function tryLoadOpentype() {
|
|
8025
8219
|
try {
|
|
@@ -8046,6 +8240,63 @@ function toArrayBuffer(data) {
|
|
|
8046
8240
|
if (data instanceof ArrayBuffer) return data;
|
|
8047
8241
|
return data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
8048
8242
|
}
|
|
8243
|
+
function parseFontBuffer(arrayBuffer, opentype) {
|
|
8244
|
+
if (isTtcBuffer(arrayBuffer)) {
|
|
8245
|
+
const results = [];
|
|
8246
|
+
for (const buf of extractTtcFonts(arrayBuffer)) {
|
|
8247
|
+
try {
|
|
8248
|
+
results.push(opentype.parse(buf));
|
|
8249
|
+
} catch {
|
|
8250
|
+
}
|
|
8251
|
+
}
|
|
8252
|
+
return results;
|
|
8253
|
+
}
|
|
8254
|
+
return [opentype.parse(arrayBuffer)];
|
|
8255
|
+
}
|
|
8256
|
+
async function createOpentypeTextMeasurerFromBuffers(fontBuffers, fontMapping) {
|
|
8257
|
+
const setup = await createOpentypeSetupFromBuffers(fontBuffers, fontMapping);
|
|
8258
|
+
return setup?.measurer ?? null;
|
|
8259
|
+
}
|
|
8260
|
+
async function createOpentypeSetupFromBuffers(fontBuffers, fontMapping) {
|
|
8261
|
+
if (fontBuffers.length === 0) return null;
|
|
8262
|
+
const opentype = await tryLoadOpentype();
|
|
8263
|
+
if (!opentype) return null;
|
|
8264
|
+
const mapping = createFontMapping(fontMapping);
|
|
8265
|
+
const reverseMap = buildReverseMapping(mapping);
|
|
8266
|
+
const measurerFonts = /* @__PURE__ */ new Map();
|
|
8267
|
+
const resolverFonts = /* @__PURE__ */ new Map();
|
|
8268
|
+
let firstMeasurerFont = null;
|
|
8269
|
+
let firstResolverFont = null;
|
|
8270
|
+
for (const buffer of fontBuffers) {
|
|
8271
|
+
try {
|
|
8272
|
+
const arrayBuffer = toArrayBuffer(buffer.data);
|
|
8273
|
+
const isTtc = isTtcBuffer(arrayBuffer);
|
|
8274
|
+
const fonts = parseFontBuffer(arrayBuffer, opentype);
|
|
8275
|
+
for (const font of fonts) {
|
|
8276
|
+
if (!firstMeasurerFont) firstMeasurerFont = font;
|
|
8277
|
+
if (!firstResolverFont) firstResolverFont = font;
|
|
8278
|
+
if (isTtc) {
|
|
8279
|
+
const fontFamily = font.names.fontFamily;
|
|
8280
|
+
if (fontFamily) {
|
|
8281
|
+
for (const name of Object.values(fontFamily)) {
|
|
8282
|
+
registerFont(name, font, reverseMap, measurerFonts, resolverFonts);
|
|
8283
|
+
}
|
|
8284
|
+
}
|
|
8285
|
+
} else if (buffer.name) {
|
|
8286
|
+
registerFont(buffer.name, font, reverseMap, measurerFonts, resolverFonts);
|
|
8287
|
+
}
|
|
8288
|
+
}
|
|
8289
|
+
} catch {
|
|
8290
|
+
}
|
|
8291
|
+
}
|
|
8292
|
+
if (measurerFonts.size === 0 && !firstMeasurerFont) return null;
|
|
8293
|
+
const measurer = new OpentypeTextMeasurer(measurerFonts, firstMeasurerFont ?? void 0);
|
|
8294
|
+
const fontResolver = new DefaultTextPathFontResolver(
|
|
8295
|
+
resolverFonts,
|
|
8296
|
+
firstResolverFont ?? void 0
|
|
8297
|
+
);
|
|
8298
|
+
return { measurer, fontResolver };
|
|
8299
|
+
}
|
|
8049
8300
|
function registerFont(name, font, reverseMap, measurerFonts, resolverFonts) {
|
|
8050
8301
|
const fullFont = font;
|
|
8051
8302
|
if (!measurerFonts.has(name)) {
|
|
@@ -8077,13 +8328,15 @@ async function createOpentypeSetupFromSystem(additionalFontDirs, fontMapping) {
|
|
|
8077
8328
|
try {
|
|
8078
8329
|
const data = await (0, import_promises.readFile)(filePath);
|
|
8079
8330
|
const arrayBuffer = toArrayBuffer(data);
|
|
8080
|
-
const
|
|
8081
|
-
|
|
8082
|
-
|
|
8083
|
-
|
|
8084
|
-
|
|
8085
|
-
|
|
8086
|
-
|
|
8331
|
+
const fonts = parseFontBuffer(arrayBuffer, opentype);
|
|
8332
|
+
for (const font of fonts) {
|
|
8333
|
+
if (!firstMeasurerFont) firstMeasurerFont = font;
|
|
8334
|
+
if (!firstResolverFont) firstResolverFont = font;
|
|
8335
|
+
const fontFamily = font.names.fontFamily;
|
|
8336
|
+
if (fontFamily) {
|
|
8337
|
+
for (const name of Object.values(fontFamily)) {
|
|
8338
|
+
registerFont(name, font, reverseMap, measurerFonts, resolverFonts);
|
|
8339
|
+
}
|
|
8087
8340
|
}
|
|
8088
8341
|
}
|
|
8089
8342
|
} catch {
|
|
@@ -8229,6 +8482,8 @@ function collectFontsFromTextBody(textBody, fonts) {
|
|
|
8229
8482
|
convertPptxToPng,
|
|
8230
8483
|
convertPptxToSvg,
|
|
8231
8484
|
createFontMapping,
|
|
8485
|
+
createOpentypeSetupFromBuffers,
|
|
8486
|
+
createOpentypeTextMeasurerFromBuffers,
|
|
8232
8487
|
getMappedFont,
|
|
8233
8488
|
getWarningEntries,
|
|
8234
8489
|
getWarningSummary
|