pptx-glimpse 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.cjs +194 -97
  2. package/dist/index.js +194 -97
  3. package/package.json +2 -2
package/dist/index.cjs CHANGED
@@ -2078,14 +2078,16 @@ function measureTextWidth(text, fontSizePt, bold, fontFamily, fontFamilyEa) {
2078
2078
  const codePoint = char.codePointAt(0);
2079
2079
  const isEa = isCjkCodePoint(codePoint);
2080
2080
  const metrics = isEa && eaMetrics ? eaMetrics : latinMetrics;
2081
+ let charWidth;
2081
2082
  if (metrics) {
2082
- totalWidth += measureCharMetrics(char, codePoint, baseSizePx, metrics);
2083
+ charWidth = measureCharMetrics(char, codePoint, baseSizePx, metrics);
2083
2084
  } else {
2084
- totalWidth += measureCharHeuristic(codePoint, baseSizePx);
2085
+ charWidth = measureCharHeuristic(codePoint, baseSizePx);
2085
2086
  }
2086
- }
2087
- if (bold) {
2088
- totalWidth *= BOLD_FACTOR;
2087
+ if (bold && !isEa) {
2088
+ charWidth *= BOLD_FACTOR;
2089
+ }
2090
+ totalWidth += charWidth;
2089
2091
  }
2090
2092
  return totalWidth;
2091
2093
  }
@@ -3811,6 +3813,65 @@ function renderPlaceholder(mimeType, w, h, transformAttr) {
3811
3813
  ].join("");
3812
3814
  }
3813
3815
 
3816
+ // src/warning-logger.ts
3817
+ var PREFIX = "[pptx-glimpse]";
3818
+ var currentLevel = "off";
3819
+ var entries = [];
3820
+ var featureCounts = /* @__PURE__ */ new Map();
3821
+ function initWarningLogger(level) {
3822
+ currentLevel = level;
3823
+ entries = [];
3824
+ featureCounts.clear();
3825
+ }
3826
+ function warn(feature, message, context) {
3827
+ if (currentLevel === "off") return;
3828
+ entries.push({ feature, message, ...context !== void 0 && { context } });
3829
+ const existing = featureCounts.get(feature);
3830
+ if (existing) {
3831
+ existing.count++;
3832
+ } else {
3833
+ featureCounts.set(feature, { message, count: 1 });
3834
+ }
3835
+ if (currentLevel === "debug") {
3836
+ const ctx = context ? ` (${context})` : "";
3837
+ console.warn(`${PREFIX} SKIP: ${feature} - ${message}${ctx}`);
3838
+ }
3839
+ }
3840
+ function debug(feature, message, context) {
3841
+ if (currentLevel !== "debug") return;
3842
+ entries.push({ feature, message, ...context !== void 0 && { context } });
3843
+ const existing = featureCounts.get(feature);
3844
+ if (existing) {
3845
+ existing.count++;
3846
+ } else {
3847
+ featureCounts.set(feature, { message, count: 1 });
3848
+ }
3849
+ const ctx = context ? ` (${context})` : "";
3850
+ console.warn(`${PREFIX} DEBUG: ${feature} - ${message}${ctx}`);
3851
+ }
3852
+ function getWarningSummary() {
3853
+ const features = [];
3854
+ for (const [feature, { message, count }] of featureCounts) {
3855
+ features.push({ feature, message, count });
3856
+ }
3857
+ return { totalCount: entries.length, features };
3858
+ }
3859
+ function flushWarnings() {
3860
+ const summary = getWarningSummary();
3861
+ if (currentLevel !== "off" && summary.features.length > 0) {
3862
+ console.warn(`${PREFIX} Summary: ${summary.features.length} unsupported feature(s) detected`);
3863
+ for (const { feature, count } of summary.features) {
3864
+ console.warn(` - ${feature}: ${count} occurrence(s)`);
3865
+ }
3866
+ }
3867
+ entries = [];
3868
+ featureCounts.clear();
3869
+ return summary;
3870
+ }
3871
+ function getWarningEntries() {
3872
+ return entries;
3873
+ }
3874
+
3814
3875
  // src/renderer/chart-renderer.ts
3815
3876
  var DEFAULT_SERIES_COLORS = [
3816
3877
  { hex: "#4472C4", alpha: 1 },
@@ -3892,11 +3953,20 @@ function renderChartTitle(title, chartWidth) {
3892
3953
  function renderBarChart(chart, x, y, w, h) {
3893
3954
  const parts = [];
3894
3955
  const { series, categories } = chart;
3895
- if (series.length === 0) return "";
3956
+ if (series.length === 0) {
3957
+ debug("chart.bar", "series is empty");
3958
+ return "";
3959
+ }
3896
3960
  const maxVal = getMaxValue(series);
3897
- if (maxVal === 0) return "";
3961
+ if (maxVal === 0) {
3962
+ debug("chart.bar", "max value is 0");
3963
+ return "";
3964
+ }
3898
3965
  const catCount = categories.length || Math.max(...series.map((s) => s.values.length));
3899
- if (catCount === 0) return "";
3966
+ if (catCount === 0) {
3967
+ debug("chart.bar", "category count is 0");
3968
+ return "";
3969
+ }
3900
3970
  const isHorizontal = chart.barDirection === "bar";
3901
3971
  parts.push(
3902
3972
  `<line x1="${round3(x)}" y1="${round3(y + h)}" x2="${round3(x + w)}" y2="${round3(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
@@ -3956,11 +4026,20 @@ function renderBarChart(chart, x, y, w, h) {
3956
4026
  function renderLineChart(chart, x, y, w, h) {
3957
4027
  const parts = [];
3958
4028
  const { series, categories } = chart;
3959
- if (series.length === 0) return "";
4029
+ if (series.length === 0) {
4030
+ debug("chart.line", "series is empty");
4031
+ return "";
4032
+ }
3960
4033
  const maxVal = getMaxValue(series);
3961
- if (maxVal === 0) return "";
4034
+ if (maxVal === 0) {
4035
+ debug("chart.line", "max value is 0");
4036
+ return "";
4037
+ }
3962
4038
  const catCount = categories.length || Math.max(...series.map((s) => s.values.length));
3963
- if (catCount === 0) return "";
4039
+ if (catCount === 0) {
4040
+ debug("chart.line", "category count is 0");
4041
+ return "";
4042
+ }
3964
4043
  parts.push(
3965
4044
  `<line x1="${round3(x)}" y1="${round3(y + h)}" x2="${round3(x + w)}" y2="${round3(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
3966
4045
  );
@@ -3996,11 +4075,20 @@ function renderLineChart(chart, x, y, w, h) {
3996
4075
  function renderAreaChart(chart, x, y, w, h) {
3997
4076
  const parts = [];
3998
4077
  const { series, categories } = chart;
3999
- if (series.length === 0) return "";
4078
+ if (series.length === 0) {
4079
+ debug("chart.area", "series is empty");
4080
+ return "";
4081
+ }
4000
4082
  const maxVal = getMaxValue(series);
4001
- if (maxVal === 0) return "";
4083
+ if (maxVal === 0) {
4084
+ debug("chart.area", "max value is 0");
4085
+ return "";
4086
+ }
4002
4087
  const catCount = categories.length || Math.max(...series.map((s) => s.values.length));
4003
- if (catCount === 0) return "";
4088
+ if (catCount === 0) {
4089
+ debug("chart.area", "category count is 0");
4090
+ return "";
4091
+ }
4004
4092
  parts.push(
4005
4093
  `<line x1="${round3(x)}" y1="${round3(y + h)}" x2="${round3(x + w)}" y2="${round3(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
4006
4094
  );
@@ -4037,9 +4125,15 @@ function renderAreaChart(chart, x, y, w, h) {
4037
4125
  function renderPieChart(chart, x, y, w, h) {
4038
4126
  const parts = [];
4039
4127
  const series = chart.series[0];
4040
- if (!series || series.values.length === 0) return "";
4128
+ if (!series || series.values.length === 0) {
4129
+ debug("chart.pie", "series is empty or has no values");
4130
+ return "";
4131
+ }
4041
4132
  const total = series.values.reduce((sum, v) => sum + v, 0);
4042
- if (total === 0) return "";
4133
+ if (total === 0) {
4134
+ debug("chart.pie", "total value is 0");
4135
+ return "";
4136
+ }
4043
4137
  const cx = x + w / 2;
4044
4138
  const cy = y + h / 2;
4045
4139
  const r = Math.min(w, h) / 2 * 0.85;
@@ -4069,9 +4163,15 @@ function renderPieChart(chart, x, y, w, h) {
4069
4163
  function renderDoughnutChart(chart, x, y, w, h) {
4070
4164
  const parts = [];
4071
4165
  const series = chart.series[0];
4072
- if (!series || series.values.length === 0) return "";
4166
+ if (!series || series.values.length === 0) {
4167
+ debug("chart.doughnut", "series is empty or has no values");
4168
+ return "";
4169
+ }
4073
4170
  const total = series.values.reduce((sum, v) => sum + v, 0);
4074
- if (total === 0) return "";
4171
+ if (total === 0) {
4172
+ debug("chart.doughnut", "total value is 0");
4173
+ return "";
4174
+ }
4075
4175
  const cx = x + w / 2;
4076
4176
  const cy = y + h / 2;
4077
4177
  const outerR = Math.min(w, h) / 2 * 0.85;
@@ -4110,7 +4210,10 @@ function renderDoughnutChart(chart, x, y, w, h) {
4110
4210
  function renderScatterChart(chart, x, y, w, h) {
4111
4211
  const parts = [];
4112
4212
  const { series } = chart;
4113
- if (series.length === 0) return "";
4213
+ if (series.length === 0) {
4214
+ debug("chart.scatter", "series is empty");
4215
+ return "";
4216
+ }
4114
4217
  let maxX = 0;
4115
4218
  let maxY = 0;
4116
4219
  for (const s of series) {
@@ -4142,7 +4245,10 @@ function renderScatterChart(chart, x, y, w, h) {
4142
4245
  function renderBubbleChart(chart, x, y, w, h) {
4143
4246
  const parts = [];
4144
4247
  const { series } = chart;
4145
- if (series.length === 0) return "";
4248
+ if (series.length === 0) {
4249
+ debug("chart.bubble", "series is empty");
4250
+ return "";
4251
+ }
4146
4252
  let maxX = 0;
4147
4253
  let maxY = 0;
4148
4254
  let maxBubble = 0;
@@ -4184,11 +4290,20 @@ function renderBubbleChart(chart, x, y, w, h) {
4184
4290
  function renderRadarChart(chart, x, y, w, h) {
4185
4291
  const parts = [];
4186
4292
  const { series, categories } = chart;
4187
- if (series.length === 0) return "";
4293
+ if (series.length === 0) {
4294
+ debug("chart.radar", "series is empty");
4295
+ return "";
4296
+ }
4188
4297
  const maxVal = getMaxValue(series);
4189
- if (maxVal === 0) return "";
4298
+ if (maxVal === 0) {
4299
+ debug("chart.radar", "max value is 0");
4300
+ return "";
4301
+ }
4190
4302
  const catCount = categories.length || Math.max(...series.map((s) => s.values.length));
4191
- if (catCount === 0) return "";
4303
+ if (catCount === 0) {
4304
+ debug("chart.radar", "category count is 0");
4305
+ return "";
4306
+ }
4192
4307
  const cx = x + w / 2;
4193
4308
  const cy = y + h / 2;
4194
4309
  const radius = Math.min(w, h) / 2 * 0.85;
@@ -4252,12 +4367,18 @@ function renderRadarChart(chart, x, y, w, h) {
4252
4367
  function renderStockChart(chart, x, y, w, h) {
4253
4368
  const parts = [];
4254
4369
  const { series, categories } = chart;
4255
- if (series.length < 3) return "";
4370
+ if (series.length < 3) {
4371
+ debug("chart.stock", `insufficient series count: ${series.length} (need at least 3)`);
4372
+ return "";
4373
+ }
4256
4374
  const highSeries = series[0];
4257
4375
  const lowSeries = series[1];
4258
4376
  const closeSeries = series[2];
4259
4377
  const catCount = categories.length || highSeries.values.length;
4260
- if (catCount === 0) return "";
4378
+ if (catCount === 0) {
4379
+ debug("chart.stock", "category count is 0");
4380
+ return "";
4381
+ }
4261
4382
  let maxVal = 0;
4262
4383
  let minVal = Infinity;
4263
4384
  for (const s of [highSeries, lowSeries, closeSeries]) {
@@ -4266,7 +4387,10 @@ function renderStockChart(chart, x, y, w, h) {
4266
4387
  minVal = Math.min(minVal, v);
4267
4388
  }
4268
4389
  }
4269
- if (maxVal === minVal) return "";
4390
+ if (maxVal === minVal) {
4391
+ debug("chart.stock", "max equals min value");
4392
+ return "";
4393
+ }
4270
4394
  parts.push(
4271
4395
  `<line x1="${round3(x)}" y1="${round3(y + h)}" x2="${round3(x + w)}" y2="${round3(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
4272
4396
  );
@@ -4302,10 +4426,16 @@ function renderStockChart(chart, x, y, w, h) {
4302
4426
  function renderSurfaceChart(chart, x, y, w, h) {
4303
4427
  const parts = [];
4304
4428
  const { series, categories } = chart;
4305
- if (series.length === 0) return "";
4429
+ if (series.length === 0) {
4430
+ debug("chart.surface", "series is empty");
4431
+ return "";
4432
+ }
4306
4433
  const rows = series.length;
4307
4434
  const cols = categories.length || Math.max(...series.map((s) => s.values.length));
4308
- if (cols === 0) return "";
4435
+ if (cols === 0) {
4436
+ debug("chart.surface", "column count is 0");
4437
+ return "";
4438
+ }
4309
4439
  let minVal = Infinity;
4310
4440
  let maxVal = -Infinity;
4311
4441
  for (const s of series) {
@@ -4378,9 +4508,15 @@ function heatmapColor(t) {
4378
4508
  function renderOfPieChart(chart, x, y, w, h) {
4379
4509
  const parts = [];
4380
4510
  const series = chart.series[0];
4381
- if (!series || series.values.length === 0) return "";
4511
+ if (!series || series.values.length === 0) {
4512
+ debug("chart.ofPie", "series is empty or has no values");
4513
+ return "";
4514
+ }
4382
4515
  const total = series.values.reduce((sum, v) => sum + v, 0);
4383
- if (total === 0) return "";
4516
+ if (total === 0) {
4517
+ debug("chart.ofPie", "total value is 0");
4518
+ return "";
4519
+ }
4384
4520
  const splitPos = chart.splitPos ?? 2;
4385
4521
  const secondPieSize = chart.secondPieSize ?? 75;
4386
4522
  const isBarOfPie = chart.ofPieType === "bar";
@@ -4745,65 +4881,6 @@ async function svgToPng(svgString, options) {
4745
4881
  return { png: result.data, width: result.info.width, height: result.info.height };
4746
4882
  }
4747
4883
 
4748
- // src/warning-logger.ts
4749
- var PREFIX = "[pptx-glimpse]";
4750
- var currentLevel = "off";
4751
- var entries = [];
4752
- var featureCounts = /* @__PURE__ */ new Map();
4753
- function initWarningLogger(level) {
4754
- currentLevel = level;
4755
- entries = [];
4756
- featureCounts.clear();
4757
- }
4758
- function warn(feature, message, context) {
4759
- if (currentLevel === "off") return;
4760
- entries.push({ feature, message, ...context !== void 0 && { context } });
4761
- const existing = featureCounts.get(feature);
4762
- if (existing) {
4763
- existing.count++;
4764
- } else {
4765
- featureCounts.set(feature, { message, count: 1 });
4766
- }
4767
- if (currentLevel === "debug") {
4768
- const ctx = context ? ` (${context})` : "";
4769
- console.warn(`${PREFIX} SKIP: ${feature} - ${message}${ctx}`);
4770
- }
4771
- }
4772
- function debug(feature, message, context) {
4773
- if (currentLevel !== "debug") return;
4774
- entries.push({ feature, message, ...context !== void 0 && { context } });
4775
- const existing = featureCounts.get(feature);
4776
- if (existing) {
4777
- existing.count++;
4778
- } else {
4779
- featureCounts.set(feature, { message, count: 1 });
4780
- }
4781
- const ctx = context ? ` (${context})` : "";
4782
- console.warn(`${PREFIX} DEBUG: ${feature} - ${message}${ctx}`);
4783
- }
4784
- function getWarningSummary() {
4785
- const features = [];
4786
- for (const [feature, { message, count }] of featureCounts) {
4787
- features.push({ feature, message, count });
4788
- }
4789
- return { totalCount: entries.length, features };
4790
- }
4791
- function flushWarnings() {
4792
- const summary = getWarningSummary();
4793
- if (currentLevel !== "off" && summary.features.length > 0) {
4794
- console.warn(`${PREFIX} Summary: ${summary.features.length} unsupported feature(s) detected`);
4795
- for (const { feature, count } of summary.features) {
4796
- console.warn(` - ${feature}: ${count} occurrence(s)`);
4797
- }
4798
- }
4799
- entries = [];
4800
- featureCounts.clear();
4801
- return summary;
4802
- }
4803
- function getWarningEntries() {
4804
- return entries;
4805
- }
4806
-
4807
4884
  // src/parser/pptx-reader.ts
4808
4885
  var import_fflate = require("fflate");
4809
4886
  function readPptx(input) {
@@ -6181,10 +6258,16 @@ function parseCustomGeometry(custGeom) {
6181
6258
  for (const path of paths) {
6182
6259
  const w = Number(path["@_w"] ?? 0);
6183
6260
  const h = Number(path["@_h"] ?? 0);
6184
- if (w === 0 && h === 0) continue;
6261
+ if (w === 0 && h === 0) {
6262
+ debug("custGeom.path", "path skipped: width and height are both 0");
6263
+ continue;
6264
+ }
6185
6265
  const vars = evaluateGuides(avGd, gdGd, w, h);
6186
6266
  const commands = buildPathCommands(path, vars);
6187
- if (!commands) continue;
6267
+ if (!commands) {
6268
+ debug("custGeom.path", "path skipped: failed to build path commands");
6269
+ continue;
6270
+ }
6188
6271
  result.push({ width: w, height: h, commands });
6189
6272
  }
6190
6273
  return result.length > 0 ? result : null;
@@ -6757,7 +6840,10 @@ function parseImage(pic, rels, slidePath, archive, colorResolver) {
6757
6840
  if (!rel) return null;
6758
6841
  const mediaPath = resolveRelationshipTarget(slidePath, rel.target);
6759
6842
  const mediaData = archive.media.get(mediaPath);
6760
- if (!mediaData) return null;
6843
+ if (!mediaData) {
6844
+ debug("picture.media", `media file not found: ${mediaPath}`);
6845
+ return null;
6846
+ }
6761
6847
  const ext = mediaPath.split(".").pop()?.toLowerCase() ?? "png";
6762
6848
  const mimeMap = {
6763
6849
  png: "image/png",
@@ -6956,9 +7042,15 @@ function parseSmartArt(graphicData, transform, rels, slidePath, archive, colorRe
6956
7042
  }
6957
7043
  }
6958
7044
  }
6959
- if (!drawingPath) return null;
7045
+ if (!drawingPath) {
7046
+ debug("smartArt.drawing", "diagramDrawing relationship not found");
7047
+ return null;
7048
+ }
6960
7049
  const drawingXml = archive.files.get(drawingPath);
6961
- if (!drawingXml) return null;
7050
+ if (!drawingXml) {
7051
+ debug("smartArt.drawing", `drawing XML not found in archive: ${drawingPath}`);
7052
+ return null;
7053
+ }
6962
7054
  const parsed = parseXml(drawingXml);
6963
7055
  const drawing = parsed.drawing;
6964
7056
  const spTree = drawing?.spTree;
@@ -7939,11 +8031,16 @@ var OpentypeTextMeasurer = class {
7939
8031
  const fontSizePx = fontSizePt * PX_PER_PT3;
7940
8032
  const scale = fontSizePx / font.unitsPerEm;
7941
8033
  let totalWidth = 0;
8034
+ const chars = [...text];
7942
8035
  const glyphs = font.stringToGlyphs(text);
7943
- for (const glyph of glyphs) {
7944
- totalWidth += (glyph.advanceWidth ?? font.unitsPerEm * 0.6) * scale;
8036
+ for (let i = 0; i < glyphs.length; i++) {
8037
+ let charWidth = (glyphs[i].advanceWidth ?? font.unitsPerEm * 0.6) * scale;
8038
+ const codePoint = chars[i]?.codePointAt(0);
8039
+ if (bold && (codePoint === void 0 || !isCjkCodePoint(codePoint))) {
8040
+ charWidth *= BOLD_FACTOR2;
8041
+ }
8042
+ totalWidth += charWidth;
7945
8043
  }
7946
- if (bold) totalWidth *= BOLD_FACTOR2;
7947
8044
  return totalWidth;
7948
8045
  }
7949
8046
  getLineHeightRatio(fontFamily, fontFamilyEa) {
package/dist/index.js CHANGED
@@ -2035,14 +2035,16 @@ function measureTextWidth(text, fontSizePt, bold, fontFamily, fontFamilyEa) {
2035
2035
  const codePoint = char.codePointAt(0);
2036
2036
  const isEa = isCjkCodePoint(codePoint);
2037
2037
  const metrics = isEa && eaMetrics ? eaMetrics : latinMetrics;
2038
+ let charWidth;
2038
2039
  if (metrics) {
2039
- totalWidth += measureCharMetrics(char, codePoint, baseSizePx, metrics);
2040
+ charWidth = measureCharMetrics(char, codePoint, baseSizePx, metrics);
2040
2041
  } else {
2041
- totalWidth += measureCharHeuristic(codePoint, baseSizePx);
2042
+ charWidth = measureCharHeuristic(codePoint, baseSizePx);
2042
2043
  }
2043
- }
2044
- if (bold) {
2045
- totalWidth *= BOLD_FACTOR;
2044
+ if (bold && !isEa) {
2045
+ charWidth *= BOLD_FACTOR;
2046
+ }
2047
+ totalWidth += charWidth;
2046
2048
  }
2047
2049
  return totalWidth;
2048
2050
  }
@@ -3768,6 +3770,65 @@ function renderPlaceholder(mimeType, w, h, transformAttr) {
3768
3770
  ].join("");
3769
3771
  }
3770
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
+
3771
3832
  // src/renderer/chart-renderer.ts
3772
3833
  var DEFAULT_SERIES_COLORS = [
3773
3834
  { hex: "#4472C4", alpha: 1 },
@@ -3849,11 +3910,20 @@ function renderChartTitle(title, chartWidth) {
3849
3910
  function renderBarChart(chart, x, y, w, h) {
3850
3911
  const parts = [];
3851
3912
  const { series, categories } = chart;
3852
- if (series.length === 0) return "";
3913
+ if (series.length === 0) {
3914
+ debug("chart.bar", "series is empty");
3915
+ return "";
3916
+ }
3853
3917
  const maxVal = getMaxValue(series);
3854
- if (maxVal === 0) return "";
3918
+ if (maxVal === 0) {
3919
+ debug("chart.bar", "max value is 0");
3920
+ return "";
3921
+ }
3855
3922
  const catCount = categories.length || Math.max(...series.map((s) => s.values.length));
3856
- if (catCount === 0) return "";
3923
+ if (catCount === 0) {
3924
+ debug("chart.bar", "category count is 0");
3925
+ return "";
3926
+ }
3857
3927
  const isHorizontal = chart.barDirection === "bar";
3858
3928
  parts.push(
3859
3929
  `<line x1="${round3(x)}" y1="${round3(y + h)}" x2="${round3(x + w)}" y2="${round3(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
@@ -3913,11 +3983,20 @@ function renderBarChart(chart, x, y, w, h) {
3913
3983
  function renderLineChart(chart, x, y, w, h) {
3914
3984
  const parts = [];
3915
3985
  const { series, categories } = chart;
3916
- if (series.length === 0) return "";
3986
+ if (series.length === 0) {
3987
+ debug("chart.line", "series is empty");
3988
+ return "";
3989
+ }
3917
3990
  const maxVal = getMaxValue(series);
3918
- if (maxVal === 0) return "";
3991
+ if (maxVal === 0) {
3992
+ debug("chart.line", "max value is 0");
3993
+ return "";
3994
+ }
3919
3995
  const catCount = categories.length || Math.max(...series.map((s) => s.values.length));
3920
- if (catCount === 0) return "";
3996
+ if (catCount === 0) {
3997
+ debug("chart.line", "category count is 0");
3998
+ return "";
3999
+ }
3921
4000
  parts.push(
3922
4001
  `<line x1="${round3(x)}" y1="${round3(y + h)}" x2="${round3(x + w)}" y2="${round3(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
3923
4002
  );
@@ -3953,11 +4032,20 @@ function renderLineChart(chart, x, y, w, h) {
3953
4032
  function renderAreaChart(chart, x, y, w, h) {
3954
4033
  const parts = [];
3955
4034
  const { series, categories } = chart;
3956
- if (series.length === 0) return "";
4035
+ if (series.length === 0) {
4036
+ debug("chart.area", "series is empty");
4037
+ return "";
4038
+ }
3957
4039
  const maxVal = getMaxValue(series);
3958
- if (maxVal === 0) return "";
4040
+ if (maxVal === 0) {
4041
+ debug("chart.area", "max value is 0");
4042
+ return "";
4043
+ }
3959
4044
  const catCount = categories.length || Math.max(...series.map((s) => s.values.length));
3960
- if (catCount === 0) return "";
4045
+ if (catCount === 0) {
4046
+ debug("chart.area", "category count is 0");
4047
+ return "";
4048
+ }
3961
4049
  parts.push(
3962
4050
  `<line x1="${round3(x)}" y1="${round3(y + h)}" x2="${round3(x + w)}" y2="${round3(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
3963
4051
  );
@@ -3994,9 +4082,15 @@ function renderAreaChart(chart, x, y, w, h) {
3994
4082
  function renderPieChart(chart, x, y, w, h) {
3995
4083
  const parts = [];
3996
4084
  const series = chart.series[0];
3997
- 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
+ }
3998
4089
  const total = series.values.reduce((sum, v) => sum + v, 0);
3999
- if (total === 0) return "";
4090
+ if (total === 0) {
4091
+ debug("chart.pie", "total value is 0");
4092
+ return "";
4093
+ }
4000
4094
  const cx = x + w / 2;
4001
4095
  const cy = y + h / 2;
4002
4096
  const r = Math.min(w, h) / 2 * 0.85;
@@ -4026,9 +4120,15 @@ function renderPieChart(chart, x, y, w, h) {
4026
4120
  function renderDoughnutChart(chart, x, y, w, h) {
4027
4121
  const parts = [];
4028
4122
  const series = chart.series[0];
4029
- 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
+ }
4030
4127
  const total = series.values.reduce((sum, v) => sum + v, 0);
4031
- if (total === 0) return "";
4128
+ if (total === 0) {
4129
+ debug("chart.doughnut", "total value is 0");
4130
+ return "";
4131
+ }
4032
4132
  const cx = x + w / 2;
4033
4133
  const cy = y + h / 2;
4034
4134
  const outerR = Math.min(w, h) / 2 * 0.85;
@@ -4067,7 +4167,10 @@ function renderDoughnutChart(chart, x, y, w, h) {
4067
4167
  function renderScatterChart(chart, x, y, w, h) {
4068
4168
  const parts = [];
4069
4169
  const { series } = chart;
4070
- if (series.length === 0) return "";
4170
+ if (series.length === 0) {
4171
+ debug("chart.scatter", "series is empty");
4172
+ return "";
4173
+ }
4071
4174
  let maxX = 0;
4072
4175
  let maxY = 0;
4073
4176
  for (const s of series) {
@@ -4099,7 +4202,10 @@ function renderScatterChart(chart, x, y, w, h) {
4099
4202
  function renderBubbleChart(chart, x, y, w, h) {
4100
4203
  const parts = [];
4101
4204
  const { series } = chart;
4102
- if (series.length === 0) return "";
4205
+ if (series.length === 0) {
4206
+ debug("chart.bubble", "series is empty");
4207
+ return "";
4208
+ }
4103
4209
  let maxX = 0;
4104
4210
  let maxY = 0;
4105
4211
  let maxBubble = 0;
@@ -4141,11 +4247,20 @@ function renderBubbleChart(chart, x, y, w, h) {
4141
4247
  function renderRadarChart(chart, x, y, w, h) {
4142
4248
  const parts = [];
4143
4249
  const { series, categories } = chart;
4144
- if (series.length === 0) return "";
4250
+ if (series.length === 0) {
4251
+ debug("chart.radar", "series is empty");
4252
+ return "";
4253
+ }
4145
4254
  const maxVal = getMaxValue(series);
4146
- if (maxVal === 0) return "";
4255
+ if (maxVal === 0) {
4256
+ debug("chart.radar", "max value is 0");
4257
+ return "";
4258
+ }
4147
4259
  const catCount = categories.length || Math.max(...series.map((s) => s.values.length));
4148
- if (catCount === 0) return "";
4260
+ if (catCount === 0) {
4261
+ debug("chart.radar", "category count is 0");
4262
+ return "";
4263
+ }
4149
4264
  const cx = x + w / 2;
4150
4265
  const cy = y + h / 2;
4151
4266
  const radius = Math.min(w, h) / 2 * 0.85;
@@ -4209,12 +4324,18 @@ function renderRadarChart(chart, x, y, w, h) {
4209
4324
  function renderStockChart(chart, x, y, w, h) {
4210
4325
  const parts = [];
4211
4326
  const { series, categories } = chart;
4212
- 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
+ }
4213
4331
  const highSeries = series[0];
4214
4332
  const lowSeries = series[1];
4215
4333
  const closeSeries = series[2];
4216
4334
  const catCount = categories.length || highSeries.values.length;
4217
- if (catCount === 0) return "";
4335
+ if (catCount === 0) {
4336
+ debug("chart.stock", "category count is 0");
4337
+ return "";
4338
+ }
4218
4339
  let maxVal = 0;
4219
4340
  let minVal = Infinity;
4220
4341
  for (const s of [highSeries, lowSeries, closeSeries]) {
@@ -4223,7 +4344,10 @@ function renderStockChart(chart, x, y, w, h) {
4223
4344
  minVal = Math.min(minVal, v);
4224
4345
  }
4225
4346
  }
4226
- if (maxVal === minVal) return "";
4347
+ if (maxVal === minVal) {
4348
+ debug("chart.stock", "max equals min value");
4349
+ return "";
4350
+ }
4227
4351
  parts.push(
4228
4352
  `<line x1="${round3(x)}" y1="${round3(y + h)}" x2="${round3(x + w)}" y2="${round3(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
4229
4353
  );
@@ -4259,10 +4383,16 @@ function renderStockChart(chart, x, y, w, h) {
4259
4383
  function renderSurfaceChart(chart, x, y, w, h) {
4260
4384
  const parts = [];
4261
4385
  const { series, categories } = chart;
4262
- if (series.length === 0) return "";
4386
+ if (series.length === 0) {
4387
+ debug("chart.surface", "series is empty");
4388
+ return "";
4389
+ }
4263
4390
  const rows = series.length;
4264
4391
  const cols = categories.length || Math.max(...series.map((s) => s.values.length));
4265
- if (cols === 0) return "";
4392
+ if (cols === 0) {
4393
+ debug("chart.surface", "column count is 0");
4394
+ return "";
4395
+ }
4266
4396
  let minVal = Infinity;
4267
4397
  let maxVal = -Infinity;
4268
4398
  for (const s of series) {
@@ -4335,9 +4465,15 @@ function heatmapColor(t) {
4335
4465
  function renderOfPieChart(chart, x, y, w, h) {
4336
4466
  const parts = [];
4337
4467
  const series = chart.series[0];
4338
- 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
+ }
4339
4472
  const total = series.values.reduce((sum, v) => sum + v, 0);
4340
- if (total === 0) return "";
4473
+ if (total === 0) {
4474
+ debug("chart.ofPie", "total value is 0");
4475
+ return "";
4476
+ }
4341
4477
  const splitPos = chart.splitPos ?? 2;
4342
4478
  const secondPieSize = chart.secondPieSize ?? 75;
4343
4479
  const isBarOfPie = chart.ofPieType === "bar";
@@ -4702,65 +4838,6 @@ async function svgToPng(svgString, options) {
4702
4838
  return { png: result.data, width: result.info.width, height: result.info.height };
4703
4839
  }
4704
4840
 
4705
- // src/warning-logger.ts
4706
- var PREFIX = "[pptx-glimpse]";
4707
- var currentLevel = "off";
4708
- var entries = [];
4709
- var featureCounts = /* @__PURE__ */ new Map();
4710
- function initWarningLogger(level) {
4711
- currentLevel = level;
4712
- entries = [];
4713
- featureCounts.clear();
4714
- }
4715
- function warn(feature, message, context) {
4716
- if (currentLevel === "off") return;
4717
- entries.push({ feature, message, ...context !== void 0 && { context } });
4718
- const existing = featureCounts.get(feature);
4719
- if (existing) {
4720
- existing.count++;
4721
- } else {
4722
- featureCounts.set(feature, { message, count: 1 });
4723
- }
4724
- if (currentLevel === "debug") {
4725
- const ctx = context ? ` (${context})` : "";
4726
- console.warn(`${PREFIX} SKIP: ${feature} - ${message}${ctx}`);
4727
- }
4728
- }
4729
- function debug(feature, message, context) {
4730
- if (currentLevel !== "debug") return;
4731
- entries.push({ feature, message, ...context !== void 0 && { context } });
4732
- const existing = featureCounts.get(feature);
4733
- if (existing) {
4734
- existing.count++;
4735
- } else {
4736
- featureCounts.set(feature, { message, count: 1 });
4737
- }
4738
- const ctx = context ? ` (${context})` : "";
4739
- console.warn(`${PREFIX} DEBUG: ${feature} - ${message}${ctx}`);
4740
- }
4741
- function getWarningSummary() {
4742
- const features = [];
4743
- for (const [feature, { message, count }] of featureCounts) {
4744
- features.push({ feature, message, count });
4745
- }
4746
- return { totalCount: entries.length, features };
4747
- }
4748
- function flushWarnings() {
4749
- const summary = getWarningSummary();
4750
- if (currentLevel !== "off" && summary.features.length > 0) {
4751
- console.warn(`${PREFIX} Summary: ${summary.features.length} unsupported feature(s) detected`);
4752
- for (const { feature, count } of summary.features) {
4753
- console.warn(` - ${feature}: ${count} occurrence(s)`);
4754
- }
4755
- }
4756
- entries = [];
4757
- featureCounts.clear();
4758
- return summary;
4759
- }
4760
- function getWarningEntries() {
4761
- return entries;
4762
- }
4763
-
4764
4841
  // src/parser/pptx-reader.ts
4765
4842
  import { unzipSync, strFromU8 } from "fflate";
4766
4843
  function readPptx(input) {
@@ -6138,10 +6215,16 @@ function parseCustomGeometry(custGeom) {
6138
6215
  for (const path of paths) {
6139
6216
  const w = Number(path["@_w"] ?? 0);
6140
6217
  const h = Number(path["@_h"] ?? 0);
6141
- 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
+ }
6142
6222
  const vars = evaluateGuides(avGd, gdGd, w, h);
6143
6223
  const commands = buildPathCommands(path, vars);
6144
- if (!commands) continue;
6224
+ if (!commands) {
6225
+ debug("custGeom.path", "path skipped: failed to build path commands");
6226
+ continue;
6227
+ }
6145
6228
  result.push({ width: w, height: h, commands });
6146
6229
  }
6147
6230
  return result.length > 0 ? result : null;
@@ -6714,7 +6797,10 @@ function parseImage(pic, rels, slidePath, archive, colorResolver) {
6714
6797
  if (!rel) return null;
6715
6798
  const mediaPath = resolveRelationshipTarget(slidePath, rel.target);
6716
6799
  const mediaData = archive.media.get(mediaPath);
6717
- if (!mediaData) return null;
6800
+ if (!mediaData) {
6801
+ debug("picture.media", `media file not found: ${mediaPath}`);
6802
+ return null;
6803
+ }
6718
6804
  const ext = mediaPath.split(".").pop()?.toLowerCase() ?? "png";
6719
6805
  const mimeMap = {
6720
6806
  png: "image/png",
@@ -6913,9 +6999,15 @@ function parseSmartArt(graphicData, transform, rels, slidePath, archive, colorRe
6913
6999
  }
6914
7000
  }
6915
7001
  }
6916
- if (!drawingPath) return null;
7002
+ if (!drawingPath) {
7003
+ debug("smartArt.drawing", "diagramDrawing relationship not found");
7004
+ return null;
7005
+ }
6917
7006
  const drawingXml = archive.files.get(drawingPath);
6918
- if (!drawingXml) return null;
7007
+ if (!drawingXml) {
7008
+ debug("smartArt.drawing", `drawing XML not found in archive: ${drawingPath}`);
7009
+ return null;
7010
+ }
6919
7011
  const parsed = parseXml(drawingXml);
6920
7012
  const drawing = parsed.drawing;
6921
7013
  const spTree = drawing?.spTree;
@@ -7896,11 +7988,16 @@ var OpentypeTextMeasurer = class {
7896
7988
  const fontSizePx = fontSizePt * PX_PER_PT3;
7897
7989
  const scale = fontSizePx / font.unitsPerEm;
7898
7990
  let totalWidth = 0;
7991
+ const chars = [...text];
7899
7992
  const glyphs = font.stringToGlyphs(text);
7900
- for (const glyph of glyphs) {
7901
- totalWidth += (glyph.advanceWidth ?? font.unitsPerEm * 0.6) * scale;
7993
+ for (let i = 0; i < glyphs.length; i++) {
7994
+ let charWidth = (glyphs[i].advanceWidth ?? font.unitsPerEm * 0.6) * scale;
7995
+ const codePoint = chars[i]?.codePointAt(0);
7996
+ if (bold && (codePoint === void 0 || !isCjkCodePoint(codePoint))) {
7997
+ charWidth *= BOLD_FACTOR2;
7998
+ }
7999
+ totalWidth += charWidth;
7902
8000
  }
7903
- if (bold) totalWidth *= BOLD_FACTOR2;
7904
8001
  return totalWidth;
7905
8002
  }
7906
8003
  getLineHeightRatio(fontFamily, fontFamilyEa) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pptx-glimpse",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "A Node.js library to render PPTX as SVG / PNG.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -62,7 +62,7 @@
62
62
  "author": "hirokisakabe",
63
63
  "license": "MIT",
64
64
  "dependencies": {
65
- "fast-xml-parser": "^4.5.0",
65
+ "fast-xml-parser": "^5.3.6",
66
66
  "fflate": "^0.8.2",
67
67
  "opentype.js": "^1.3.4",
68
68
  "sharp": "^0.34.5"