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/dist/index.js CHANGED
@@ -3770,6 +3770,65 @@ function renderPlaceholder(mimeType, w, h, transformAttr) {
3770
3770
  ].join("");
3771
3771
  }
3772
3772
 
3773
+ // src/warning-logger.ts
3774
+ var PREFIX = "[pptx-glimpse]";
3775
+ var currentLevel = "off";
3776
+ var entries = [];
3777
+ var featureCounts = /* @__PURE__ */ new Map();
3778
+ function initWarningLogger(level) {
3779
+ currentLevel = level;
3780
+ entries = [];
3781
+ featureCounts.clear();
3782
+ }
3783
+ function warn(feature, message, context) {
3784
+ if (currentLevel === "off") return;
3785
+ entries.push({ feature, message, ...context !== void 0 && { context } });
3786
+ const existing = featureCounts.get(feature);
3787
+ if (existing) {
3788
+ existing.count++;
3789
+ } else {
3790
+ featureCounts.set(feature, { message, count: 1 });
3791
+ }
3792
+ if (currentLevel === "debug") {
3793
+ const ctx = context ? ` (${context})` : "";
3794
+ console.warn(`${PREFIX} SKIP: ${feature} - ${message}${ctx}`);
3795
+ }
3796
+ }
3797
+ function debug(feature, message, context) {
3798
+ if (currentLevel !== "debug") return;
3799
+ entries.push({ feature, message, ...context !== void 0 && { context } });
3800
+ const existing = featureCounts.get(feature);
3801
+ if (existing) {
3802
+ existing.count++;
3803
+ } else {
3804
+ featureCounts.set(feature, { message, count: 1 });
3805
+ }
3806
+ const ctx = context ? ` (${context})` : "";
3807
+ console.warn(`${PREFIX} DEBUG: ${feature} - ${message}${ctx}`);
3808
+ }
3809
+ function getWarningSummary() {
3810
+ const features = [];
3811
+ for (const [feature, { message, count }] of featureCounts) {
3812
+ features.push({ feature, message, count });
3813
+ }
3814
+ return { totalCount: entries.length, features };
3815
+ }
3816
+ function flushWarnings() {
3817
+ const summary = getWarningSummary();
3818
+ if (currentLevel !== "off" && summary.features.length > 0) {
3819
+ console.warn(`${PREFIX} Summary: ${summary.features.length} unsupported feature(s) detected`);
3820
+ for (const { feature, count } of summary.features) {
3821
+ console.warn(` - ${feature}: ${count} occurrence(s)`);
3822
+ }
3823
+ }
3824
+ entries = [];
3825
+ featureCounts.clear();
3826
+ return summary;
3827
+ }
3828
+ function getWarningEntries() {
3829
+ return entries;
3830
+ }
3831
+
3773
3832
  // src/renderer/chart-renderer.ts
3774
3833
  var DEFAULT_SERIES_COLORS = [
3775
3834
  { hex: "#4472C4", alpha: 1 },
@@ -3851,11 +3910,20 @@ function renderChartTitle(title, chartWidth) {
3851
3910
  function renderBarChart(chart, x, y, w, h) {
3852
3911
  const parts = [];
3853
3912
  const { series, categories } = chart;
3854
- if (series.length === 0) return "";
3913
+ if (series.length === 0) {
3914
+ debug("chart.bar", "series is empty");
3915
+ return "";
3916
+ }
3855
3917
  const maxVal = getMaxValue(series);
3856
- if (maxVal === 0) return "";
3918
+ if (maxVal === 0) {
3919
+ debug("chart.bar", "max value is 0");
3920
+ return "";
3921
+ }
3857
3922
  const catCount = categories.length || Math.max(...series.map((s) => s.values.length));
3858
- if (catCount === 0) return "";
3923
+ if (catCount === 0) {
3924
+ debug("chart.bar", "category count is 0");
3925
+ return "";
3926
+ }
3859
3927
  const isHorizontal = chart.barDirection === "bar";
3860
3928
  parts.push(
3861
3929
  `<line x1="${round3(x)}" y1="${round3(y + h)}" x2="${round3(x + w)}" y2="${round3(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
@@ -3915,11 +3983,20 @@ function renderBarChart(chart, x, y, w, h) {
3915
3983
  function renderLineChart(chart, x, y, w, h) {
3916
3984
  const parts = [];
3917
3985
  const { series, categories } = chart;
3918
- if (series.length === 0) return "";
3986
+ if (series.length === 0) {
3987
+ debug("chart.line", "series is empty");
3988
+ return "";
3989
+ }
3919
3990
  const maxVal = getMaxValue(series);
3920
- if (maxVal === 0) return "";
3991
+ if (maxVal === 0) {
3992
+ debug("chart.line", "max value is 0");
3993
+ return "";
3994
+ }
3921
3995
  const catCount = categories.length || Math.max(...series.map((s) => s.values.length));
3922
- if (catCount === 0) return "";
3996
+ if (catCount === 0) {
3997
+ debug("chart.line", "category count is 0");
3998
+ return "";
3999
+ }
3923
4000
  parts.push(
3924
4001
  `<line x1="${round3(x)}" y1="${round3(y + h)}" x2="${round3(x + w)}" y2="${round3(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
3925
4002
  );
@@ -3955,11 +4032,20 @@ function renderLineChart(chart, x, y, w, h) {
3955
4032
  function renderAreaChart(chart, x, y, w, h) {
3956
4033
  const parts = [];
3957
4034
  const { series, categories } = chart;
3958
- if (series.length === 0) return "";
4035
+ if (series.length === 0) {
4036
+ debug("chart.area", "series is empty");
4037
+ return "";
4038
+ }
3959
4039
  const maxVal = getMaxValue(series);
3960
- if (maxVal === 0) return "";
4040
+ if (maxVal === 0) {
4041
+ debug("chart.area", "max value is 0");
4042
+ return "";
4043
+ }
3961
4044
  const catCount = categories.length || Math.max(...series.map((s) => s.values.length));
3962
- if (catCount === 0) return "";
4045
+ if (catCount === 0) {
4046
+ debug("chart.area", "category count is 0");
4047
+ return "";
4048
+ }
3963
4049
  parts.push(
3964
4050
  `<line x1="${round3(x)}" y1="${round3(y + h)}" x2="${round3(x + w)}" y2="${round3(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
3965
4051
  );
@@ -3996,9 +4082,15 @@ function renderAreaChart(chart, x, y, w, h) {
3996
4082
  function renderPieChart(chart, x, y, w, h) {
3997
4083
  const parts = [];
3998
4084
  const series = chart.series[0];
3999
- if (!series || series.values.length === 0) return "";
4085
+ if (!series || series.values.length === 0) {
4086
+ debug("chart.pie", "series is empty or has no values");
4087
+ return "";
4088
+ }
4000
4089
  const total = series.values.reduce((sum, v) => sum + v, 0);
4001
- if (total === 0) return "";
4090
+ if (total === 0) {
4091
+ debug("chart.pie", "total value is 0");
4092
+ return "";
4093
+ }
4002
4094
  const cx = x + w / 2;
4003
4095
  const cy = y + h / 2;
4004
4096
  const r = Math.min(w, h) / 2 * 0.85;
@@ -4028,9 +4120,15 @@ function renderPieChart(chart, x, y, w, h) {
4028
4120
  function renderDoughnutChart(chart, x, y, w, h) {
4029
4121
  const parts = [];
4030
4122
  const series = chart.series[0];
4031
- if (!series || series.values.length === 0) return "";
4123
+ if (!series || series.values.length === 0) {
4124
+ debug("chart.doughnut", "series is empty or has no values");
4125
+ return "";
4126
+ }
4032
4127
  const total = series.values.reduce((sum, v) => sum + v, 0);
4033
- if (total === 0) return "";
4128
+ if (total === 0) {
4129
+ debug("chart.doughnut", "total value is 0");
4130
+ return "";
4131
+ }
4034
4132
  const cx = x + w / 2;
4035
4133
  const cy = y + h / 2;
4036
4134
  const outerR = Math.min(w, h) / 2 * 0.85;
@@ -4069,7 +4167,10 @@ function renderDoughnutChart(chart, x, y, w, h) {
4069
4167
  function renderScatterChart(chart, x, y, w, h) {
4070
4168
  const parts = [];
4071
4169
  const { series } = chart;
4072
- if (series.length === 0) return "";
4170
+ if (series.length === 0) {
4171
+ debug("chart.scatter", "series is empty");
4172
+ return "";
4173
+ }
4073
4174
  let maxX = 0;
4074
4175
  let maxY = 0;
4075
4176
  for (const s of series) {
@@ -4101,7 +4202,10 @@ function renderScatterChart(chart, x, y, w, h) {
4101
4202
  function renderBubbleChart(chart, x, y, w, h) {
4102
4203
  const parts = [];
4103
4204
  const { series } = chart;
4104
- if (series.length === 0) return "";
4205
+ if (series.length === 0) {
4206
+ debug("chart.bubble", "series is empty");
4207
+ return "";
4208
+ }
4105
4209
  let maxX = 0;
4106
4210
  let maxY = 0;
4107
4211
  let maxBubble = 0;
@@ -4143,11 +4247,20 @@ function renderBubbleChart(chart, x, y, w, h) {
4143
4247
  function renderRadarChart(chart, x, y, w, h) {
4144
4248
  const parts = [];
4145
4249
  const { series, categories } = chart;
4146
- if (series.length === 0) return "";
4250
+ if (series.length === 0) {
4251
+ debug("chart.radar", "series is empty");
4252
+ return "";
4253
+ }
4147
4254
  const maxVal = getMaxValue(series);
4148
- if (maxVal === 0) return "";
4255
+ if (maxVal === 0) {
4256
+ debug("chart.radar", "max value is 0");
4257
+ return "";
4258
+ }
4149
4259
  const catCount = categories.length || Math.max(...series.map((s) => s.values.length));
4150
- if (catCount === 0) return "";
4260
+ if (catCount === 0) {
4261
+ debug("chart.radar", "category count is 0");
4262
+ return "";
4263
+ }
4151
4264
  const cx = x + w / 2;
4152
4265
  const cy = y + h / 2;
4153
4266
  const radius = Math.min(w, h) / 2 * 0.85;
@@ -4211,12 +4324,18 @@ function renderRadarChart(chart, x, y, w, h) {
4211
4324
  function renderStockChart(chart, x, y, w, h) {
4212
4325
  const parts = [];
4213
4326
  const { series, categories } = chart;
4214
- if (series.length < 3) return "";
4327
+ if (series.length < 3) {
4328
+ debug("chart.stock", `insufficient series count: ${series.length} (need at least 3)`);
4329
+ return "";
4330
+ }
4215
4331
  const highSeries = series[0];
4216
4332
  const lowSeries = series[1];
4217
4333
  const closeSeries = series[2];
4218
4334
  const catCount = categories.length || highSeries.values.length;
4219
- if (catCount === 0) return "";
4335
+ if (catCount === 0) {
4336
+ debug("chart.stock", "category count is 0");
4337
+ return "";
4338
+ }
4220
4339
  let maxVal = 0;
4221
4340
  let minVal = Infinity;
4222
4341
  for (const s of [highSeries, lowSeries, closeSeries]) {
@@ -4225,7 +4344,10 @@ function renderStockChart(chart, x, y, w, h) {
4225
4344
  minVal = Math.min(minVal, v);
4226
4345
  }
4227
4346
  }
4228
- if (maxVal === minVal) return "";
4347
+ if (maxVal === minVal) {
4348
+ debug("chart.stock", "max equals min value");
4349
+ return "";
4350
+ }
4229
4351
  parts.push(
4230
4352
  `<line x1="${round3(x)}" y1="${round3(y + h)}" x2="${round3(x + w)}" y2="${round3(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
4231
4353
  );
@@ -4261,10 +4383,16 @@ function renderStockChart(chart, x, y, w, h) {
4261
4383
  function renderSurfaceChart(chart, x, y, w, h) {
4262
4384
  const parts = [];
4263
4385
  const { series, categories } = chart;
4264
- if (series.length === 0) return "";
4386
+ if (series.length === 0) {
4387
+ debug("chart.surface", "series is empty");
4388
+ return "";
4389
+ }
4265
4390
  const rows = series.length;
4266
4391
  const cols = categories.length || Math.max(...series.map((s) => s.values.length));
4267
- if (cols === 0) return "";
4392
+ if (cols === 0) {
4393
+ debug("chart.surface", "column count is 0");
4394
+ return "";
4395
+ }
4268
4396
  let minVal = Infinity;
4269
4397
  let maxVal = -Infinity;
4270
4398
  for (const s of series) {
@@ -4337,9 +4465,15 @@ function heatmapColor(t) {
4337
4465
  function renderOfPieChart(chart, x, y, w, h) {
4338
4466
  const parts = [];
4339
4467
  const series = chart.series[0];
4340
- if (!series || series.values.length === 0) return "";
4468
+ if (!series || series.values.length === 0) {
4469
+ debug("chart.ofPie", "series is empty or has no values");
4470
+ return "";
4471
+ }
4341
4472
  const total = series.values.reduce((sum, v) => sum + v, 0);
4342
- if (total === 0) return "";
4473
+ if (total === 0) {
4474
+ debug("chart.ofPie", "total value is 0");
4475
+ return "";
4476
+ }
4343
4477
  const splitPos = chart.splitPos ?? 2;
4344
4478
  const secondPieSize = chart.secondPieSize ?? 75;
4345
4479
  const isBarOfPie = chart.ofPieType === "bar";
@@ -4704,65 +4838,6 @@ async function svgToPng(svgString, options) {
4704
4838
  return { png: result.data, width: result.info.width, height: result.info.height };
4705
4839
  }
4706
4840
 
4707
- // src/warning-logger.ts
4708
- var PREFIX = "[pptx-glimpse]";
4709
- var currentLevel = "off";
4710
- var entries = [];
4711
- var featureCounts = /* @__PURE__ */ new Map();
4712
- function initWarningLogger(level) {
4713
- currentLevel = level;
4714
- entries = [];
4715
- featureCounts.clear();
4716
- }
4717
- function warn(feature, message, context) {
4718
- if (currentLevel === "off") return;
4719
- entries.push({ feature, message, ...context !== void 0 && { context } });
4720
- const existing = featureCounts.get(feature);
4721
- if (existing) {
4722
- existing.count++;
4723
- } else {
4724
- featureCounts.set(feature, { message, count: 1 });
4725
- }
4726
- if (currentLevel === "debug") {
4727
- const ctx = context ? ` (${context})` : "";
4728
- console.warn(`${PREFIX} SKIP: ${feature} - ${message}${ctx}`);
4729
- }
4730
- }
4731
- function debug(feature, message, context) {
4732
- if (currentLevel !== "debug") return;
4733
- entries.push({ feature, message, ...context !== void 0 && { context } });
4734
- const existing = featureCounts.get(feature);
4735
- if (existing) {
4736
- existing.count++;
4737
- } else {
4738
- featureCounts.set(feature, { message, count: 1 });
4739
- }
4740
- const ctx = context ? ` (${context})` : "";
4741
- console.warn(`${PREFIX} DEBUG: ${feature} - ${message}${ctx}`);
4742
- }
4743
- function getWarningSummary() {
4744
- const features = [];
4745
- for (const [feature, { message, count }] of featureCounts) {
4746
- features.push({ feature, message, count });
4747
- }
4748
- return { totalCount: entries.length, features };
4749
- }
4750
- function flushWarnings() {
4751
- const summary = getWarningSummary();
4752
- if (currentLevel !== "off" && summary.features.length > 0) {
4753
- console.warn(`${PREFIX} Summary: ${summary.features.length} unsupported feature(s) detected`);
4754
- for (const { feature, count } of summary.features) {
4755
- console.warn(` - ${feature}: ${count} occurrence(s)`);
4756
- }
4757
- }
4758
- entries = [];
4759
- featureCounts.clear();
4760
- return summary;
4761
- }
4762
- function getWarningEntries() {
4763
- return entries;
4764
- }
4765
-
4766
4841
  // src/parser/pptx-reader.ts
4767
4842
  import { unzipSync, strFromU8 } from "fflate";
4768
4843
  function readPptx(input) {
@@ -6140,10 +6215,16 @@ function parseCustomGeometry(custGeom) {
6140
6215
  for (const path of paths) {
6141
6216
  const w = Number(path["@_w"] ?? 0);
6142
6217
  const h = Number(path["@_h"] ?? 0);
6143
- if (w === 0 && h === 0) continue;
6218
+ if (w === 0 && h === 0) {
6219
+ debug("custGeom.path", "path skipped: width and height are both 0");
6220
+ continue;
6221
+ }
6144
6222
  const vars = evaluateGuides(avGd, gdGd, w, h);
6145
6223
  const commands = buildPathCommands(path, vars);
6146
- if (!commands) continue;
6224
+ if (!commands) {
6225
+ debug("custGeom.path", "path skipped: failed to build path commands");
6226
+ continue;
6227
+ }
6147
6228
  result.push({ width: w, height: h, commands });
6148
6229
  }
6149
6230
  return result.length > 0 ? result : null;
@@ -6716,7 +6797,10 @@ function parseImage(pic, rels, slidePath, archive, colorResolver) {
6716
6797
  if (!rel) return null;
6717
6798
  const mediaPath = resolveRelationshipTarget(slidePath, rel.target);
6718
6799
  const mediaData = archive.media.get(mediaPath);
6719
- if (!mediaData) return null;
6800
+ if (!mediaData) {
6801
+ debug("picture.media", `media file not found: ${mediaPath}`);
6802
+ return null;
6803
+ }
6720
6804
  const ext = mediaPath.split(".").pop()?.toLowerCase() ?? "png";
6721
6805
  const mimeMap = {
6722
6806
  png: "image/png",
@@ -6915,9 +6999,15 @@ function parseSmartArt(graphicData, transform, rels, slidePath, archive, colorRe
6915
6999
  }
6916
7000
  }
6917
7001
  }
6918
- if (!drawingPath) return null;
7002
+ if (!drawingPath) {
7003
+ debug("smartArt.drawing", "diagramDrawing relationship not found");
7004
+ return null;
7005
+ }
6919
7006
  const drawingXml = archive.files.get(drawingPath);
6920
- if (!drawingXml) return null;
7007
+ if (!drawingXml) {
7008
+ debug("smartArt.drawing", `drawing XML not found in archive: ${drawingPath}`);
7009
+ return null;
7010
+ }
6921
7011
  const parsed = parseXml(drawingXml);
6922
7012
  const drawing = parsed.drawing;
6923
7013
  const spTree = drawing?.spTree;
@@ -7977,6 +8067,108 @@ function collectFontFilePaths(additionalDirs) {
7977
8067
  return result;
7978
8068
  }
7979
8069
 
8070
+ // src/font/ttc-parser.ts
8071
+ var TTC_TAG = 1953784678;
8072
+ function isTtcBuffer(data) {
8073
+ const view = toDataView(data);
8074
+ if (view.byteLength < 4) return false;
8075
+ return view.getUint32(0) === TTC_TAG;
8076
+ }
8077
+ function extractTtcFonts(data) {
8078
+ const view = toDataView(data);
8079
+ const bytes = toUint8Array(data);
8080
+ if (view.byteLength < 12) return [];
8081
+ if (view.getUint32(0) !== TTC_TAG) return [];
8082
+ const numFonts = view.getUint32(8);
8083
+ if (numFonts === 0) return [];
8084
+ const headerEnd = 12 + numFonts * 4;
8085
+ if (view.byteLength < headerEnd) return [];
8086
+ const results = [];
8087
+ for (let i = 0; i < numFonts; i++) {
8088
+ try {
8089
+ const fontOffset = view.getUint32(12 + i * 4);
8090
+ const extracted = extractSingleFont(view, bytes, fontOffset);
8091
+ if (extracted) results.push(extracted);
8092
+ } catch {
8093
+ }
8094
+ }
8095
+ return results;
8096
+ }
8097
+ function extractSingleFont(view, bytes, fontOffset) {
8098
+ if (fontOffset + 12 > view.byteLength) return null;
8099
+ const sfVersion = view.getUint32(fontOffset);
8100
+ const numTables = view.getUint16(fontOffset + 4);
8101
+ if (numTables === 0) return null;
8102
+ const tableRecordsStart = fontOffset + 12;
8103
+ const tableRecordsEnd = tableRecordsStart + numTables * 16;
8104
+ if (tableRecordsEnd > view.byteLength) return null;
8105
+ const tables = [];
8106
+ for (let i = 0; i < numTables; i++) {
8107
+ const recOffset = tableRecordsStart + i * 16;
8108
+ const tableOffset = view.getUint32(recOffset + 8);
8109
+ const tableLength = view.getUint32(recOffset + 12);
8110
+ if (tableOffset > view.byteLength || tableLength > view.byteLength - tableOffset) {
8111
+ return null;
8112
+ }
8113
+ tables.push({
8114
+ tag: view.getUint32(recOffset),
8115
+ checkSum: view.getUint32(recOffset + 4),
8116
+ offset: tableOffset,
8117
+ length: tableLength
8118
+ });
8119
+ }
8120
+ const headerSize = 12 + numTables * 16;
8121
+ let dataSize = 0;
8122
+ for (const table of tables) {
8123
+ dataSize += alignTo4(table.length);
8124
+ }
8125
+ const totalSize = headerSize + dataSize;
8126
+ const output = new ArrayBuffer(totalSize);
8127
+ const outView = new DataView(output);
8128
+ const outBytes = new Uint8Array(output);
8129
+ outView.setUint32(0, sfVersion);
8130
+ outView.setUint16(4, numTables);
8131
+ const { searchRange, entrySelector, rangeShift } = calcOffsetTableFields(numTables);
8132
+ outView.setUint16(6, searchRange);
8133
+ outView.setUint16(8, entrySelector);
8134
+ outView.setUint16(10, rangeShift);
8135
+ let currentDataOffset = headerSize;
8136
+ for (let i = 0; i < numTables; i++) {
8137
+ const table = tables[i];
8138
+ const recOffset = 12 + i * 16;
8139
+ outView.setUint32(recOffset, table.tag);
8140
+ outView.setUint32(recOffset + 4, table.checkSum);
8141
+ outView.setUint32(recOffset + 8, currentDataOffset);
8142
+ outView.setUint32(recOffset + 12, table.length);
8143
+ outBytes.set(bytes.subarray(table.offset, table.offset + table.length), currentDataOffset);
8144
+ currentDataOffset += alignTo4(table.length);
8145
+ }
8146
+ return output;
8147
+ }
8148
+ function alignTo4(n) {
8149
+ return n + 3 & ~3;
8150
+ }
8151
+ function calcOffsetTableFields(numTables) {
8152
+ let searchRange = 16;
8153
+ let entrySelector = 0;
8154
+ while (searchRange * 2 <= numTables * 16) {
8155
+ searchRange *= 2;
8156
+ entrySelector++;
8157
+ }
8158
+ const rangeShift = numTables * 16 - searchRange;
8159
+ return { searchRange, entrySelector, rangeShift };
8160
+ }
8161
+ function toDataView(data) {
8162
+ if (data instanceof Uint8Array) {
8163
+ return new DataView(data.buffer, data.byteOffset, data.byteLength);
8164
+ }
8165
+ return new DataView(data);
8166
+ }
8167
+ function toUint8Array(data) {
8168
+ if (data instanceof Uint8Array) return data;
8169
+ return new Uint8Array(data);
8170
+ }
8171
+
7980
8172
  // src/font/opentype-helpers.ts
7981
8173
  async function tryLoadOpentype() {
7982
8174
  try {
@@ -8003,6 +8195,63 @@ function toArrayBuffer(data) {
8003
8195
  if (data instanceof ArrayBuffer) return data;
8004
8196
  return data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
8005
8197
  }
8198
+ function parseFontBuffer(arrayBuffer, opentype) {
8199
+ if (isTtcBuffer(arrayBuffer)) {
8200
+ const results = [];
8201
+ for (const buf of extractTtcFonts(arrayBuffer)) {
8202
+ try {
8203
+ results.push(opentype.parse(buf));
8204
+ } catch {
8205
+ }
8206
+ }
8207
+ return results;
8208
+ }
8209
+ return [opentype.parse(arrayBuffer)];
8210
+ }
8211
+ async function createOpentypeTextMeasurerFromBuffers(fontBuffers, fontMapping) {
8212
+ const setup = await createOpentypeSetupFromBuffers(fontBuffers, fontMapping);
8213
+ return setup?.measurer ?? null;
8214
+ }
8215
+ async function createOpentypeSetupFromBuffers(fontBuffers, fontMapping) {
8216
+ if (fontBuffers.length === 0) return null;
8217
+ const opentype = await tryLoadOpentype();
8218
+ if (!opentype) return null;
8219
+ const mapping = createFontMapping(fontMapping);
8220
+ const reverseMap = buildReverseMapping(mapping);
8221
+ const measurerFonts = /* @__PURE__ */ new Map();
8222
+ const resolverFonts = /* @__PURE__ */ new Map();
8223
+ let firstMeasurerFont = null;
8224
+ let firstResolverFont = null;
8225
+ for (const buffer of fontBuffers) {
8226
+ try {
8227
+ const arrayBuffer = toArrayBuffer(buffer.data);
8228
+ const isTtc = isTtcBuffer(arrayBuffer);
8229
+ const fonts = parseFontBuffer(arrayBuffer, opentype);
8230
+ for (const font of fonts) {
8231
+ if (!firstMeasurerFont) firstMeasurerFont = font;
8232
+ if (!firstResolverFont) firstResolverFont = font;
8233
+ if (isTtc) {
8234
+ const fontFamily = font.names.fontFamily;
8235
+ if (fontFamily) {
8236
+ for (const name of Object.values(fontFamily)) {
8237
+ registerFont(name, font, reverseMap, measurerFonts, resolverFonts);
8238
+ }
8239
+ }
8240
+ } else if (buffer.name) {
8241
+ registerFont(buffer.name, font, reverseMap, measurerFonts, resolverFonts);
8242
+ }
8243
+ }
8244
+ } catch {
8245
+ }
8246
+ }
8247
+ if (measurerFonts.size === 0 && !firstMeasurerFont) return null;
8248
+ const measurer = new OpentypeTextMeasurer(measurerFonts, firstMeasurerFont ?? void 0);
8249
+ const fontResolver = new DefaultTextPathFontResolver(
8250
+ resolverFonts,
8251
+ firstResolverFont ?? void 0
8252
+ );
8253
+ return { measurer, fontResolver };
8254
+ }
8006
8255
  function registerFont(name, font, reverseMap, measurerFonts, resolverFonts) {
8007
8256
  const fullFont = font;
8008
8257
  if (!measurerFonts.has(name)) {
@@ -8034,13 +8283,15 @@ async function createOpentypeSetupFromSystem(additionalFontDirs, fontMapping) {
8034
8283
  try {
8035
8284
  const data = await readFile(filePath);
8036
8285
  const arrayBuffer = toArrayBuffer(data);
8037
- const font = opentype.parse(arrayBuffer);
8038
- if (!firstMeasurerFont) firstMeasurerFont = font;
8039
- if (!firstResolverFont) firstResolverFont = font;
8040
- const fontFamily = font.names.fontFamily;
8041
- if (fontFamily) {
8042
- for (const name of Object.values(fontFamily)) {
8043
- registerFont(name, font, reverseMap, measurerFonts, resolverFonts);
8286
+ const fonts = parseFontBuffer(arrayBuffer, opentype);
8287
+ for (const font of fonts) {
8288
+ if (!firstMeasurerFont) firstMeasurerFont = font;
8289
+ if (!firstResolverFont) firstResolverFont = font;
8290
+ const fontFamily = font.names.fontFamily;
8291
+ if (fontFamily) {
8292
+ for (const name of Object.values(fontFamily)) {
8293
+ registerFont(name, font, reverseMap, measurerFonts, resolverFonts);
8294
+ }
8044
8295
  }
8045
8296
  }
8046
8297
  } catch {
@@ -8185,6 +8436,8 @@ export {
8185
8436
  convertPptxToPng,
8186
8437
  convertPptxToSvg,
8187
8438
  createFontMapping,
8439
+ createOpentypeSetupFromBuffers,
8440
+ createOpentypeTextMeasurerFromBuffers,
8188
8441
  getMappedFont,
8189
8442
  getWarningEntries,
8190
8443
  getWarningSummary
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pptx-glimpse",
3
- "version": "0.2.1",
4
- "description": "A Node.js library to render PPTX as SVG / PNG.",
3
+ "version": "0.3.0",
4
+ "description": "A lightweight JavaScript library for rendering PowerPoint (.pptx) files as SVG or PNG in Node.js. No LibreOffice required.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
7
7
  "module": "./dist/index.js",
@@ -24,6 +24,7 @@
24
24
  "test:coverage": "vitest run --coverage",
25
25
  "test:watch": "vitest",
26
26
  "typecheck": "tsc --noEmit",
27
+ "knip": "knip",
27
28
  "dev": "tsx scripts/dev-server.ts",
28
29
  "render": "tsx scripts/test-render.ts",
29
30
  "inspect": "tsx scripts/inspect-pptx.ts",
@@ -75,6 +76,7 @@
75
76
  "eslint": "^9.0.0",
76
77
  "eslint-config-prettier": "^10.0.0",
77
78
  "jszip": "^3.10.1",
79
+ "knip": "^5.85.0",
78
80
  "pixelmatch": "^7.1.0",
79
81
  "prettier": "^3.0.0",
80
82
  "tsup": "^8.0.0",