pptx-glimpse 0.6.0 → 0.7.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 CHANGED
@@ -29,7 +29,8 @@ __export(index_exports, {
29
29
  createOpentypeTextMeasurerFromBuffers: () => createOpentypeTextMeasurerFromBuffers,
30
30
  getMappedFont: () => getMappedFont,
31
31
  getWarningEntries: () => getWarningEntries,
32
- getWarningSummary: () => getWarningSummary
32
+ getWarningSummary: () => getWarningSummary,
33
+ initResvgWasm: () => initResvgWasm
33
34
  });
34
35
  module.exports = __toCommonJS(index_exports);
35
36
 
@@ -1570,15 +1571,36 @@ function parseXmlOrdered(xml) {
1570
1571
  }
1571
1572
 
1572
1573
  // src/png/png-converter.ts
1573
- var import_resvg_js = require("@resvg/resvg-js");
1574
+ var import_resvg_wasm = require("@resvg/resvg-wasm");
1575
+ var import_promises2 = require("fs/promises");
1576
+ var import_module = require("module");
1577
+ var import_meta = {};
1578
+ var wasmInitPromise = null;
1579
+ function resolveWasmPath() {
1580
+ const baseUrl = import_meta.url || `file://${__filename}`;
1581
+ const require2 = (0, import_module.createRequire)(baseUrl);
1582
+ return require2.resolve("@resvg/resvg-wasm/index_bg.wasm");
1583
+ }
1584
+ async function initResvgWasm() {
1585
+ if (!wasmInitPromise) {
1586
+ wasmInitPromise = (async () => {
1587
+ const wasmPath = resolveWasmPath();
1588
+ const wasmBuffer = await (0, import_promises2.readFile)(wasmPath);
1589
+ await (0, import_resvg_wasm.initWasm)(wasmBuffer);
1590
+ })();
1591
+ }
1592
+ await wasmInitPromise;
1593
+ }
1574
1594
  async function svgToPng(svgString, options) {
1595
+ await initResvgWasm();
1575
1596
  const resvgOptions = {};
1576
1597
  if (options?.width) {
1577
1598
  resvgOptions.fitTo = { mode: "width", value: options.width };
1578
1599
  } else if (options?.height) {
1579
1600
  resvgOptions.fitTo = { mode: "height", value: options.height };
1580
1601
  }
1581
- const rendered = await (0, import_resvg_js.renderAsync)(svgString, resvgOptions);
1602
+ const resvg = new import_resvg_wasm.Resvg(svgString, resvgOptions);
1603
+ const rendered = resvg.render();
1582
1604
  return {
1583
1605
  png: Buffer.from(rendered.asPng()),
1584
1606
  width: rendered.width,
@@ -3017,7 +3039,7 @@ function navigateOrdered(ordered, path) {
3017
3039
  }
3018
3040
  return current;
3019
3041
  }
3020
- function parseSlide(slideXml, slidePath, slideNumber, archive, colorResolver, fontScheme, fmtScheme) {
3042
+ function parseSlide(slideXml, slidePath, slideNumber, archive, colorResolver, fontScheme, fmtScheme, placeholderStyles) {
3021
3043
  const parsed = parseXml(slideXml);
3022
3044
  const sld = parsed.sld;
3023
3045
  if (!sld) {
@@ -3041,7 +3063,8 @@ function parseSlide(slideXml, slidePath, slideNumber, archive, colorResolver, fo
3041
3063
  fillContext,
3042
3064
  fontScheme,
3043
3065
  orderedSpTree,
3044
- fmtScheme
3066
+ fmtScheme,
3067
+ placeholderStyles
3045
3068
  );
3046
3069
  const showMasterSpAttr = sld?.["@_showMasterSp"];
3047
3070
  const showMasterSp = showMasterSpAttr !== "0" && showMasterSpAttr !== "false";
@@ -3068,7 +3091,7 @@ function mergeChildElements(spTree, source) {
3068
3091
  }
3069
3092
  }
3070
3093
  }
3071
- function parseShapeTree(spTree, rels, slidePath, archive, colorResolver, context, fillContext, fontScheme, orderedChildren, fmtScheme) {
3094
+ function parseShapeTree(spTree, rels, slidePath, archive, colorResolver, context, fillContext, fontScheme, orderedChildren, fmtScheme, placeholderStyles) {
3072
3095
  if (!spTree) return [];
3073
3096
  if (orderedChildren) {
3074
3097
  return parseShapeTreeOrdered(
@@ -3081,7 +3104,8 @@ function parseShapeTree(spTree, rels, slidePath, archive, colorResolver, context
3081
3104
  context,
3082
3105
  fillContext,
3083
3106
  fontScheme,
3084
- fmtScheme
3107
+ fmtScheme,
3108
+ placeholderStyles
3085
3109
  );
3086
3110
  }
3087
3111
  const alternateContents = spTree.AlternateContent ?? [];
@@ -3102,7 +3126,8 @@ function parseShapeTree(spTree, rels, slidePath, archive, colorResolver, context
3102
3126
  fillContext,
3103
3127
  fontScheme,
3104
3128
  void 0,
3105
- fmtScheme
3129
+ fmtScheme,
3130
+ placeholderStyles
3106
3131
  );
3107
3132
  if (shape) {
3108
3133
  elements.push(shape);
@@ -3158,7 +3183,7 @@ function parseShapeTree(spTree, rels, slidePath, archive, colorResolver, context
3158
3183
  }
3159
3184
  return elements;
3160
3185
  }
3161
- function parseShapeTreeOrdered(spTree, orderedChildren, rels, slidePath, archive, colorResolver, context, fillContext, fontScheme, fmtScheme) {
3186
+ function parseShapeTreeOrdered(spTree, orderedChildren, rels, slidePath, archive, colorResolver, context, fillContext, fontScheme, fmtScheme, placeholderStyles) {
3162
3187
  const ctx = context ?? slidePath;
3163
3188
  const elements = [];
3164
3189
  const tagCounters = {};
@@ -3200,7 +3225,8 @@ function parseShapeTreeOrdered(spTree, orderedChildren, rels, slidePath, archive
3200
3225
  ctx,
3201
3226
  fillContext,
3202
3227
  fontScheme,
3203
- fmtScheme
3228
+ fmtScheme,
3229
+ placeholderStyles
3204
3230
  );
3205
3231
  }
3206
3232
  }
@@ -3224,12 +3250,13 @@ function parseShapeTreeOrdered(spTree, orderedChildren, rels, slidePath, archive
3224
3250
  ctx,
3225
3251
  fillContext,
3226
3252
  fontScheme,
3227
- fmtScheme
3253
+ fmtScheme,
3254
+ placeholderStyles
3228
3255
  );
3229
3256
  }
3230
3257
  return elements;
3231
3258
  }
3232
- function parseAndPushElement(tag, element, orderedNode, elements, rels, slidePath, archive, colorResolver, ctx, fillContext, fontScheme, fmtScheme) {
3259
+ function parseAndPushElement(tag, element, orderedNode, elements, rels, slidePath, archive, colorResolver, ctx, fillContext, fontScheme, fmtScheme, placeholderStyles) {
3233
3260
  switch (tag) {
3234
3261
  case "sp": {
3235
3262
  const shape = parseShape(
@@ -3239,7 +3266,8 @@ function parseAndPushElement(tag, element, orderedNode, elements, rels, slidePat
3239
3266
  fillContext,
3240
3267
  fontScheme,
3241
3268
  orderedNode,
3242
- fmtScheme
3269
+ fmtScheme,
3270
+ placeholderStyles
3243
3271
  );
3244
3272
  if (shape) {
3245
3273
  elements.push(shape);
@@ -3304,22 +3332,42 @@ function parseAndPushElement(tag, element, orderedNode, elements, rels, slidePat
3304
3332
  }
3305
3333
  }
3306
3334
  }
3307
- function parseShape(sp, colorResolver, rels, fillContext, fontScheme, orderedNode, fmtScheme) {
3335
+ function parseShape(sp, colorResolver, rels, fillContext, fontScheme, orderedNode, fmtScheme, placeholderStyles) {
3308
3336
  const spPr = sp.spPr;
3309
- if (!spPr) return null;
3310
- const transform = parseTransform(spPr.xfrm);
3337
+ const spPrIsObject = spPr != null && typeof spPr === "object";
3338
+ const nvSpPr = sp.nvSpPr;
3339
+ const nvPr = nvSpPr?.nvPr;
3340
+ const ph = nvPr?.ph;
3341
+ const placeholderType = ph ? ph["@_type"] ?? "body" : void 0;
3342
+ const placeholderIdx = ph?.["@_idx"] !== void 0 ? Number(ph["@_idx"]) : void 0;
3343
+ let transform = null;
3344
+ let geometry;
3345
+ if (spPrIsObject) {
3346
+ transform = parseTransform(spPr.xfrm);
3347
+ geometry = parseGeometry(spPr);
3348
+ } else {
3349
+ geometry = { type: "preset", preset: "rect", adjustValues: {} };
3350
+ }
3351
+ if (!transform && placeholderType && placeholderStyles) {
3352
+ const inherited = findMatchingPlaceholder(placeholderType, placeholderIdx, placeholderStyles);
3353
+ if (inherited?.transform) {
3354
+ transform = inherited.transform;
3355
+ }
3356
+ if (!spPrIsObject && inherited?.geometry) {
3357
+ geometry = inherited.geometry;
3358
+ }
3359
+ }
3311
3360
  if (!transform) return null;
3312
- if (spPr.scene3d) {
3361
+ if (spPrIsObject && spPr.scene3d) {
3313
3362
  warn("spPr.scene3d", "3D scene/camera not implemented");
3314
3363
  }
3315
- if (spPr.sp3d) {
3364
+ if (spPrIsObject && spPr.sp3d) {
3316
3365
  warn("spPr.sp3d", "3D extrusion/bevel not implemented");
3317
3366
  }
3318
- const geometry = parseGeometry(spPr);
3319
3367
  const styleRef = resolveShapeStyle(sp.style, fmtScheme, colorResolver);
3320
- const directFill = parseFillFromNode(spPr, colorResolver, fillContext);
3368
+ const directFill = spPrIsObject ? parseFillFromNode(spPr, colorResolver, fillContext) : null;
3321
3369
  const fill = directFill ?? styleRef?.fill ?? null;
3322
- const directOutline = parseOutline(spPr.ln, colorResolver);
3370
+ const directOutline = spPrIsObject ? parseOutline(spPr.ln, colorResolver) : null;
3323
3371
  const outline = directOutline ?? styleRef?.outline ?? null;
3324
3372
  let orderedTxBody;
3325
3373
  if (orderedNode) {
@@ -3337,16 +3385,11 @@ function parseShape(sp, colorResolver, rels, fillContext, fontScheme, orderedNod
3337
3385
  void 0,
3338
3386
  orderedTxBody
3339
3387
  );
3340
- const directEffects = parseEffectList(spPr.effectLst, colorResolver);
3388
+ const directEffects = spPrIsObject ? parseEffectList(spPr.effectLst, colorResolver) : null;
3341
3389
  const effects = directEffects ?? styleRef?.effects ?? null;
3342
- const nvSpPr = sp.nvSpPr;
3343
3390
  const cNvPr = nvSpPr?.cNvPr;
3344
3391
  const altText = cNvPr?.["@_descr"];
3345
3392
  const hyperlink = parseHyperlink(cNvPr?.hlinkClick, rels);
3346
- const nvPr = nvSpPr?.nvPr;
3347
- const ph = nvPr?.ph;
3348
- const placeholderType = ph ? ph["@_type"] ?? "body" : void 0;
3349
- const placeholderIdx = ph?.["@_idx"] !== void 0 ? Number(ph["@_idx"]) : void 0;
3350
3393
  return {
3351
3394
  type: "shape",
3352
3395
  transform,
@@ -3700,6 +3743,31 @@ function parseGeometry(spPr) {
3700
3743
  }
3701
3744
  return { type: "preset", preset: "rect", adjustValues: {} };
3702
3745
  }
3746
+ function findMatchingPlaceholder(placeholderType, placeholderIdx, styles) {
3747
+ if (placeholderIdx !== void 0) {
3748
+ const byIdx = styles.find((s) => s.placeholderIdx === placeholderIdx && s.transform);
3749
+ if (byIdx) return byIdx;
3750
+ const byIdxAny = styles.find((s) => s.placeholderIdx === placeholderIdx);
3751
+ if (byIdxAny) return byIdxAny;
3752
+ }
3753
+ const byTypeWithTransform = styles.find(
3754
+ (s) => s.placeholderType === placeholderType && s.transform
3755
+ );
3756
+ if (byTypeWithTransform) return byTypeWithTransform;
3757
+ const fallbackType = placeholderType === "ctrTitle" ? "title" : placeholderType === "subTitle" ? "body" : void 0;
3758
+ if (fallbackType) {
3759
+ const byFallbackWithTransform = styles.find(
3760
+ (s) => s.placeholderType === fallbackType && s.transform
3761
+ );
3762
+ if (byFallbackWithTransform) return byFallbackWithTransform;
3763
+ }
3764
+ const byType = styles.find((s) => s.placeholderType === placeholderType);
3765
+ if (byType) return byType;
3766
+ if (fallbackType) {
3767
+ return styles.find((s) => s.placeholderType === fallbackType);
3768
+ }
3769
+ return void 0;
3770
+ }
3703
3771
  function parseTextBody(txBody, colorResolver, rels, fontScheme, lstStyleOverride, orderedTxBody) {
3704
3772
  if (!txBody) return null;
3705
3773
  const bodyPr = txBody.bodyPr;
@@ -4129,10 +4197,15 @@ function parseSlideLayoutPlaceholderStyles(xml, colorResolver) {
4129
4197
  const txBody = sp.txBody;
4130
4198
  const lstStyleNode = txBody?.lstStyle;
4131
4199
  const lstStyle = lstStyleNode ? parseListStyle(lstStyleNode, colorResolver) : void 0;
4200
+ const spPr = sp.spPr;
4201
+ const transform = spPr && typeof spPr === "object" ? parseTransform(spPr.xfrm) : null;
4202
+ const geometry = spPr && typeof spPr === "object" ? parseGeometry(spPr) : void 0;
4132
4203
  results.push({
4133
4204
  placeholderType,
4134
4205
  ...placeholderIdx !== void 0 && { placeholderIdx },
4135
- ...lstStyle && { lstStyle }
4206
+ ...lstStyle && { lstStyle },
4207
+ ...transform && { transform },
4208
+ ...geometry && { geometry }
4136
4209
  });
4137
4210
  }
4138
4211
  return results;
@@ -4249,10 +4322,15 @@ function parseSlideMasterPlaceholderStyles(xml, colorResolver) {
4249
4322
  const txBody = sp.txBody;
4250
4323
  const lstStyleNode = txBody?.lstStyle;
4251
4324
  const lstStyle = lstStyleNode ? parseListStyle(lstStyleNode, colorResolver) : void 0;
4325
+ const spPr = sp.spPr;
4326
+ const transform = spPr && typeof spPr === "object" ? parseTransform(spPr.xfrm) : null;
4327
+ const geometry = spPr && typeof spPr === "object" ? parseGeometry(spPr) : void 0;
4252
4328
  results.push({
4253
4329
  placeholderType,
4254
4330
  ...placeholderIdx !== void 0 && { placeholderIdx },
4255
- ...lstStyle && { lstStyle }
4331
+ ...lstStyle && { lstStyle },
4332
+ ...transform && { transform },
4333
+ ...geometry && { geometry }
4256
4334
  });
4257
4335
  }
4258
4336
  return results;
@@ -4703,6 +4781,17 @@ function parseSlideWithLayout(slideNumber, path, data) {
4703
4781
  slideMasterData,
4704
4782
  data.theme
4705
4783
  );
4784
+ let layoutPlaceholderStyles = [];
4785
+ let layoutShowMasterSp = true;
4786
+ if (layoutXml) {
4787
+ layoutPlaceholderStyles = parseSlideLayoutPlaceholderStyles(layoutXml, slideColorResolver);
4788
+ layoutShowMasterSp = parseSlideLayoutShowMasterSp(layoutXml);
4789
+ }
4790
+ const masterPlaceholderStyles = slideMasterData?.placeholderStyles ?? data.masterPlaceholderStyles;
4791
+ const mergedPlaceholderStyles = mergePlaceholderGeometry(
4792
+ layoutPlaceholderStyles,
4793
+ masterPlaceholderStyles
4794
+ );
4706
4795
  const slide = parseSlide(
4707
4796
  slideXml,
4708
4797
  path,
@@ -4710,11 +4799,10 @@ function parseSlideWithLayout(slideNumber, path, data) {
4710
4799
  data.archive,
4711
4800
  slideColorResolver,
4712
4801
  data.theme.fontScheme,
4713
- data.theme.fmtScheme
4802
+ data.theme.fmtScheme,
4803
+ mergedPlaceholderStyles
4714
4804
  );
4715
4805
  let layoutElements = [];
4716
- let layoutPlaceholderStyles = [];
4717
- let layoutShowMasterSp = true;
4718
4806
  if (layoutXml && layoutPath) {
4719
4807
  if (!slide.background) {
4720
4808
  const layoutFillContext = {
@@ -4736,13 +4824,10 @@ function parseSlideWithLayout(slideNumber, path, data) {
4736
4824
  data.theme.fontScheme,
4737
4825
  data.theme.fmtScheme
4738
4826
  );
4739
- layoutPlaceholderStyles = parseSlideLayoutPlaceholderStyles(layoutXml, slideColorResolver);
4740
- layoutShowMasterSp = parseSlideLayoutShowMasterSp(layoutXml);
4741
4827
  }
4742
4828
  if (!slide.background) {
4743
4829
  slide.background = slideMasterData?.background ?? data.masterBackground;
4744
4830
  }
4745
- const masterPlaceholderStyles = slideMasterData?.placeholderStyles ?? data.masterPlaceholderStyles;
4746
4831
  const masterTxStyles = slideMasterData?.txStyles ?? data.masterTxStyles;
4747
4832
  applyTextStyleInheritance(slide.elements, {
4748
4833
  layoutPlaceholderStyles,
@@ -4754,6 +4839,38 @@ function parseSlideWithLayout(slideNumber, path, data) {
4754
4839
  const masterElements = slideMasterData?.elements ?? data.masterElements;
4755
4840
  return { slide, layoutElements, layoutShowMasterSp, masterElements };
4756
4841
  }
4842
+ function mergePlaceholderGeometry(layoutStyles, masterStyles) {
4843
+ const merged = layoutStyles.map((ls) => {
4844
+ if (ls.transform) return ls;
4845
+ const masterMatch = findPlaceholderByTypeAndIdx(
4846
+ ls.placeholderType,
4847
+ ls.placeholderIdx,
4848
+ masterStyles
4849
+ );
4850
+ if (!masterMatch) return ls;
4851
+ return {
4852
+ ...ls,
4853
+ ...!ls.transform && masterMatch.transform && { transform: masterMatch.transform },
4854
+ ...!ls.geometry && masterMatch.geometry && { geometry: masterMatch.geometry }
4855
+ };
4856
+ });
4857
+ for (const ms of masterStyles) {
4858
+ const exists = merged.some(
4859
+ (m) => m.placeholderType === ms.placeholderType && m.placeholderIdx === ms.placeholderIdx
4860
+ );
4861
+ if (!exists) {
4862
+ merged.push(ms);
4863
+ }
4864
+ }
4865
+ return merged;
4866
+ }
4867
+ function findPlaceholderByTypeAndIdx(type, idx, styles) {
4868
+ if (idx !== void 0) {
4869
+ const byIdx = styles.find((s) => s.placeholderIdx === idx);
4870
+ if (byIdx) return byIdx;
4871
+ }
4872
+ return styles.find((s) => s.placeholderType === type);
4873
+ }
4757
4874
  function defaultColorScheme2() {
4758
4875
  return {
4759
4876
  dk1: "#000000",
@@ -8797,5 +8914,6 @@ function collectFontsFromTextBody(textBody, fonts) {
8797
8914
  createOpentypeTextMeasurerFromBuffers,
8798
8915
  getMappedFont,
8799
8916
  getWarningEntries,
8800
- getWarningSummary
8917
+ getWarningSummary,
8918
+ initResvgWasm
8801
8919
  });
package/dist/index.d.cts CHANGED
@@ -177,4 +177,11 @@ interface OpentypeSetup {
177
177
  */
178
178
  declare function createOpentypeSetupFromBuffers(fontBuffers: FontBuffer[], fontMapping?: FontMapping): Promise<OpentypeSetup | null>;
179
179
 
180
- export { type ConvertOptions, DEFAULT_FONT_MAPPING, type FontBuffer, type FontMapping, type LogLevel, type OpentypeSetup, type SlideImage, type SlideSvg, type UsedFonts, type WarningEntry, type WarningSummary, collectUsedFonts, convertPptxToPng, convertPptxToSvg, createFontMapping, createOpentypeSetupFromBuffers, createOpentypeTextMeasurerFromBuffers, getMappedFont, getWarningEntries, getWarningSummary };
180
+ /**
181
+ * resvg-wasm の WASM モジュールを初期化する。
182
+ * 明示的に呼び出さなくても、初回の PNG 変換時に自動的に初期化される。
183
+ * アプリケーション起動時に初期化しておきたい場合に使用する。
184
+ */
185
+ declare function initResvgWasm(): Promise<void>;
186
+
187
+ export { type ConvertOptions, DEFAULT_FONT_MAPPING, type FontBuffer, type FontMapping, type LogLevel, type OpentypeSetup, type SlideImage, type SlideSvg, type UsedFonts, type WarningEntry, type WarningSummary, collectUsedFonts, convertPptxToPng, convertPptxToSvg, createFontMapping, createOpentypeSetupFromBuffers, createOpentypeTextMeasurerFromBuffers, getMappedFont, getWarningEntries, getWarningSummary, initResvgWasm };
package/dist/index.d.ts CHANGED
@@ -177,4 +177,11 @@ interface OpentypeSetup {
177
177
  */
178
178
  declare function createOpentypeSetupFromBuffers(fontBuffers: FontBuffer[], fontMapping?: FontMapping): Promise<OpentypeSetup | null>;
179
179
 
180
- export { type ConvertOptions, DEFAULT_FONT_MAPPING, type FontBuffer, type FontMapping, type LogLevel, type OpentypeSetup, type SlideImage, type SlideSvg, type UsedFonts, type WarningEntry, type WarningSummary, collectUsedFonts, convertPptxToPng, convertPptxToSvg, createFontMapping, createOpentypeSetupFromBuffers, createOpentypeTextMeasurerFromBuffers, getMappedFont, getWarningEntries, getWarningSummary };
180
+ /**
181
+ * resvg-wasm の WASM モジュールを初期化する。
182
+ * 明示的に呼び出さなくても、初回の PNG 変換時に自動的に初期化される。
183
+ * アプリケーション起動時に初期化しておきたい場合に使用する。
184
+ */
185
+ declare function initResvgWasm(): Promise<void>;
186
+
187
+ export { type ConvertOptions, DEFAULT_FONT_MAPPING, type FontBuffer, type FontMapping, type LogLevel, type OpentypeSetup, type SlideImage, type SlideSvg, type UsedFonts, type WarningEntry, type WarningSummary, collectUsedFonts, convertPptxToPng, convertPptxToSvg, createFontMapping, createOpentypeSetupFromBuffers, createOpentypeTextMeasurerFromBuffers, getMappedFont, getWarningEntries, getWarningSummary, initResvgWasm };
package/dist/index.js CHANGED
@@ -1535,15 +1535,35 @@ function parseXmlOrdered(xml) {
1535
1535
  }
1536
1536
 
1537
1537
  // src/png/png-converter.ts
1538
- import { renderAsync } from "@resvg/resvg-js";
1538
+ import { initWasm, Resvg } from "@resvg/resvg-wasm";
1539
+ import { readFile as readFile2 } from "fs/promises";
1540
+ import { createRequire } from "module";
1541
+ var wasmInitPromise = null;
1542
+ function resolveWasmPath() {
1543
+ const baseUrl = import.meta.url || `file://${__filename}`;
1544
+ const require2 = createRequire(baseUrl);
1545
+ return require2.resolve("@resvg/resvg-wasm/index_bg.wasm");
1546
+ }
1547
+ async function initResvgWasm() {
1548
+ if (!wasmInitPromise) {
1549
+ wasmInitPromise = (async () => {
1550
+ const wasmPath = resolveWasmPath();
1551
+ const wasmBuffer = await readFile2(wasmPath);
1552
+ await initWasm(wasmBuffer);
1553
+ })();
1554
+ }
1555
+ await wasmInitPromise;
1556
+ }
1539
1557
  async function svgToPng(svgString, options) {
1558
+ await initResvgWasm();
1540
1559
  const resvgOptions = {};
1541
1560
  if (options?.width) {
1542
1561
  resvgOptions.fitTo = { mode: "width", value: options.width };
1543
1562
  } else if (options?.height) {
1544
1563
  resvgOptions.fitTo = { mode: "height", value: options.height };
1545
1564
  }
1546
- const rendered = await renderAsync(svgString, resvgOptions);
1565
+ const resvg = new Resvg(svgString, resvgOptions);
1566
+ const rendered = resvg.render();
1547
1567
  return {
1548
1568
  png: Buffer.from(rendered.asPng()),
1549
1569
  width: rendered.width,
@@ -2982,7 +3002,7 @@ function navigateOrdered(ordered, path) {
2982
3002
  }
2983
3003
  return current;
2984
3004
  }
2985
- function parseSlide(slideXml, slidePath, slideNumber, archive, colorResolver, fontScheme, fmtScheme) {
3005
+ function parseSlide(slideXml, slidePath, slideNumber, archive, colorResolver, fontScheme, fmtScheme, placeholderStyles) {
2986
3006
  const parsed = parseXml(slideXml);
2987
3007
  const sld = parsed.sld;
2988
3008
  if (!sld) {
@@ -3006,7 +3026,8 @@ function parseSlide(slideXml, slidePath, slideNumber, archive, colorResolver, fo
3006
3026
  fillContext,
3007
3027
  fontScheme,
3008
3028
  orderedSpTree,
3009
- fmtScheme
3029
+ fmtScheme,
3030
+ placeholderStyles
3010
3031
  );
3011
3032
  const showMasterSpAttr = sld?.["@_showMasterSp"];
3012
3033
  const showMasterSp = showMasterSpAttr !== "0" && showMasterSpAttr !== "false";
@@ -3033,7 +3054,7 @@ function mergeChildElements(spTree, source) {
3033
3054
  }
3034
3055
  }
3035
3056
  }
3036
- function parseShapeTree(spTree, rels, slidePath, archive, colorResolver, context, fillContext, fontScheme, orderedChildren, fmtScheme) {
3057
+ function parseShapeTree(spTree, rels, slidePath, archive, colorResolver, context, fillContext, fontScheme, orderedChildren, fmtScheme, placeholderStyles) {
3037
3058
  if (!spTree) return [];
3038
3059
  if (orderedChildren) {
3039
3060
  return parseShapeTreeOrdered(
@@ -3046,7 +3067,8 @@ function parseShapeTree(spTree, rels, slidePath, archive, colorResolver, context
3046
3067
  context,
3047
3068
  fillContext,
3048
3069
  fontScheme,
3049
- fmtScheme
3070
+ fmtScheme,
3071
+ placeholderStyles
3050
3072
  );
3051
3073
  }
3052
3074
  const alternateContents = spTree.AlternateContent ?? [];
@@ -3067,7 +3089,8 @@ function parseShapeTree(spTree, rels, slidePath, archive, colorResolver, context
3067
3089
  fillContext,
3068
3090
  fontScheme,
3069
3091
  void 0,
3070
- fmtScheme
3092
+ fmtScheme,
3093
+ placeholderStyles
3071
3094
  );
3072
3095
  if (shape) {
3073
3096
  elements.push(shape);
@@ -3123,7 +3146,7 @@ function parseShapeTree(spTree, rels, slidePath, archive, colorResolver, context
3123
3146
  }
3124
3147
  return elements;
3125
3148
  }
3126
- function parseShapeTreeOrdered(spTree, orderedChildren, rels, slidePath, archive, colorResolver, context, fillContext, fontScheme, fmtScheme) {
3149
+ function parseShapeTreeOrdered(spTree, orderedChildren, rels, slidePath, archive, colorResolver, context, fillContext, fontScheme, fmtScheme, placeholderStyles) {
3127
3150
  const ctx = context ?? slidePath;
3128
3151
  const elements = [];
3129
3152
  const tagCounters = {};
@@ -3165,7 +3188,8 @@ function parseShapeTreeOrdered(spTree, orderedChildren, rels, slidePath, archive
3165
3188
  ctx,
3166
3189
  fillContext,
3167
3190
  fontScheme,
3168
- fmtScheme
3191
+ fmtScheme,
3192
+ placeholderStyles
3169
3193
  );
3170
3194
  }
3171
3195
  }
@@ -3189,12 +3213,13 @@ function parseShapeTreeOrdered(spTree, orderedChildren, rels, slidePath, archive
3189
3213
  ctx,
3190
3214
  fillContext,
3191
3215
  fontScheme,
3192
- fmtScheme
3216
+ fmtScheme,
3217
+ placeholderStyles
3193
3218
  );
3194
3219
  }
3195
3220
  return elements;
3196
3221
  }
3197
- function parseAndPushElement(tag, element, orderedNode, elements, rels, slidePath, archive, colorResolver, ctx, fillContext, fontScheme, fmtScheme) {
3222
+ function parseAndPushElement(tag, element, orderedNode, elements, rels, slidePath, archive, colorResolver, ctx, fillContext, fontScheme, fmtScheme, placeholderStyles) {
3198
3223
  switch (tag) {
3199
3224
  case "sp": {
3200
3225
  const shape = parseShape(
@@ -3204,7 +3229,8 @@ function parseAndPushElement(tag, element, orderedNode, elements, rels, slidePat
3204
3229
  fillContext,
3205
3230
  fontScheme,
3206
3231
  orderedNode,
3207
- fmtScheme
3232
+ fmtScheme,
3233
+ placeholderStyles
3208
3234
  );
3209
3235
  if (shape) {
3210
3236
  elements.push(shape);
@@ -3269,22 +3295,42 @@ function parseAndPushElement(tag, element, orderedNode, elements, rels, slidePat
3269
3295
  }
3270
3296
  }
3271
3297
  }
3272
- function parseShape(sp, colorResolver, rels, fillContext, fontScheme, orderedNode, fmtScheme) {
3298
+ function parseShape(sp, colorResolver, rels, fillContext, fontScheme, orderedNode, fmtScheme, placeholderStyles) {
3273
3299
  const spPr = sp.spPr;
3274
- if (!spPr) return null;
3275
- const transform = parseTransform(spPr.xfrm);
3300
+ const spPrIsObject = spPr != null && typeof spPr === "object";
3301
+ const nvSpPr = sp.nvSpPr;
3302
+ const nvPr = nvSpPr?.nvPr;
3303
+ const ph = nvPr?.ph;
3304
+ const placeholderType = ph ? ph["@_type"] ?? "body" : void 0;
3305
+ const placeholderIdx = ph?.["@_idx"] !== void 0 ? Number(ph["@_idx"]) : void 0;
3306
+ let transform = null;
3307
+ let geometry;
3308
+ if (spPrIsObject) {
3309
+ transform = parseTransform(spPr.xfrm);
3310
+ geometry = parseGeometry(spPr);
3311
+ } else {
3312
+ geometry = { type: "preset", preset: "rect", adjustValues: {} };
3313
+ }
3314
+ if (!transform && placeholderType && placeholderStyles) {
3315
+ const inherited = findMatchingPlaceholder(placeholderType, placeholderIdx, placeholderStyles);
3316
+ if (inherited?.transform) {
3317
+ transform = inherited.transform;
3318
+ }
3319
+ if (!spPrIsObject && inherited?.geometry) {
3320
+ geometry = inherited.geometry;
3321
+ }
3322
+ }
3276
3323
  if (!transform) return null;
3277
- if (spPr.scene3d) {
3324
+ if (spPrIsObject && spPr.scene3d) {
3278
3325
  warn("spPr.scene3d", "3D scene/camera not implemented");
3279
3326
  }
3280
- if (spPr.sp3d) {
3327
+ if (spPrIsObject && spPr.sp3d) {
3281
3328
  warn("spPr.sp3d", "3D extrusion/bevel not implemented");
3282
3329
  }
3283
- const geometry = parseGeometry(spPr);
3284
3330
  const styleRef = resolveShapeStyle(sp.style, fmtScheme, colorResolver);
3285
- const directFill = parseFillFromNode(spPr, colorResolver, fillContext);
3331
+ const directFill = spPrIsObject ? parseFillFromNode(spPr, colorResolver, fillContext) : null;
3286
3332
  const fill = directFill ?? styleRef?.fill ?? null;
3287
- const directOutline = parseOutline(spPr.ln, colorResolver);
3333
+ const directOutline = spPrIsObject ? parseOutline(spPr.ln, colorResolver) : null;
3288
3334
  const outline = directOutline ?? styleRef?.outline ?? null;
3289
3335
  let orderedTxBody;
3290
3336
  if (orderedNode) {
@@ -3302,16 +3348,11 @@ function parseShape(sp, colorResolver, rels, fillContext, fontScheme, orderedNod
3302
3348
  void 0,
3303
3349
  orderedTxBody
3304
3350
  );
3305
- const directEffects = parseEffectList(spPr.effectLst, colorResolver);
3351
+ const directEffects = spPrIsObject ? parseEffectList(spPr.effectLst, colorResolver) : null;
3306
3352
  const effects = directEffects ?? styleRef?.effects ?? null;
3307
- const nvSpPr = sp.nvSpPr;
3308
3353
  const cNvPr = nvSpPr?.cNvPr;
3309
3354
  const altText = cNvPr?.["@_descr"];
3310
3355
  const hyperlink = parseHyperlink(cNvPr?.hlinkClick, rels);
3311
- const nvPr = nvSpPr?.nvPr;
3312
- const ph = nvPr?.ph;
3313
- const placeholderType = ph ? ph["@_type"] ?? "body" : void 0;
3314
- const placeholderIdx = ph?.["@_idx"] !== void 0 ? Number(ph["@_idx"]) : void 0;
3315
3356
  return {
3316
3357
  type: "shape",
3317
3358
  transform,
@@ -3665,6 +3706,31 @@ function parseGeometry(spPr) {
3665
3706
  }
3666
3707
  return { type: "preset", preset: "rect", adjustValues: {} };
3667
3708
  }
3709
+ function findMatchingPlaceholder(placeholderType, placeholderIdx, styles) {
3710
+ if (placeholderIdx !== void 0) {
3711
+ const byIdx = styles.find((s) => s.placeholderIdx === placeholderIdx && s.transform);
3712
+ if (byIdx) return byIdx;
3713
+ const byIdxAny = styles.find((s) => s.placeholderIdx === placeholderIdx);
3714
+ if (byIdxAny) return byIdxAny;
3715
+ }
3716
+ const byTypeWithTransform = styles.find(
3717
+ (s) => s.placeholderType === placeholderType && s.transform
3718
+ );
3719
+ if (byTypeWithTransform) return byTypeWithTransform;
3720
+ const fallbackType = placeholderType === "ctrTitle" ? "title" : placeholderType === "subTitle" ? "body" : void 0;
3721
+ if (fallbackType) {
3722
+ const byFallbackWithTransform = styles.find(
3723
+ (s) => s.placeholderType === fallbackType && s.transform
3724
+ );
3725
+ if (byFallbackWithTransform) return byFallbackWithTransform;
3726
+ }
3727
+ const byType = styles.find((s) => s.placeholderType === placeholderType);
3728
+ if (byType) return byType;
3729
+ if (fallbackType) {
3730
+ return styles.find((s) => s.placeholderType === fallbackType);
3731
+ }
3732
+ return void 0;
3733
+ }
3668
3734
  function parseTextBody(txBody, colorResolver, rels, fontScheme, lstStyleOverride, orderedTxBody) {
3669
3735
  if (!txBody) return null;
3670
3736
  const bodyPr = txBody.bodyPr;
@@ -4094,10 +4160,15 @@ function parseSlideLayoutPlaceholderStyles(xml, colorResolver) {
4094
4160
  const txBody = sp.txBody;
4095
4161
  const lstStyleNode = txBody?.lstStyle;
4096
4162
  const lstStyle = lstStyleNode ? parseListStyle(lstStyleNode, colorResolver) : void 0;
4163
+ const spPr = sp.spPr;
4164
+ const transform = spPr && typeof spPr === "object" ? parseTransform(spPr.xfrm) : null;
4165
+ const geometry = spPr && typeof spPr === "object" ? parseGeometry(spPr) : void 0;
4097
4166
  results.push({
4098
4167
  placeholderType,
4099
4168
  ...placeholderIdx !== void 0 && { placeholderIdx },
4100
- ...lstStyle && { lstStyle }
4169
+ ...lstStyle && { lstStyle },
4170
+ ...transform && { transform },
4171
+ ...geometry && { geometry }
4101
4172
  });
4102
4173
  }
4103
4174
  return results;
@@ -4214,10 +4285,15 @@ function parseSlideMasterPlaceholderStyles(xml, colorResolver) {
4214
4285
  const txBody = sp.txBody;
4215
4286
  const lstStyleNode = txBody?.lstStyle;
4216
4287
  const lstStyle = lstStyleNode ? parseListStyle(lstStyleNode, colorResolver) : void 0;
4288
+ const spPr = sp.spPr;
4289
+ const transform = spPr && typeof spPr === "object" ? parseTransform(spPr.xfrm) : null;
4290
+ const geometry = spPr && typeof spPr === "object" ? parseGeometry(spPr) : void 0;
4217
4291
  results.push({
4218
4292
  placeholderType,
4219
4293
  ...placeholderIdx !== void 0 && { placeholderIdx },
4220
- ...lstStyle && { lstStyle }
4294
+ ...lstStyle && { lstStyle },
4295
+ ...transform && { transform },
4296
+ ...geometry && { geometry }
4221
4297
  });
4222
4298
  }
4223
4299
  return results;
@@ -4668,6 +4744,17 @@ function parseSlideWithLayout(slideNumber, path, data) {
4668
4744
  slideMasterData,
4669
4745
  data.theme
4670
4746
  );
4747
+ let layoutPlaceholderStyles = [];
4748
+ let layoutShowMasterSp = true;
4749
+ if (layoutXml) {
4750
+ layoutPlaceholderStyles = parseSlideLayoutPlaceholderStyles(layoutXml, slideColorResolver);
4751
+ layoutShowMasterSp = parseSlideLayoutShowMasterSp(layoutXml);
4752
+ }
4753
+ const masterPlaceholderStyles = slideMasterData?.placeholderStyles ?? data.masterPlaceholderStyles;
4754
+ const mergedPlaceholderStyles = mergePlaceholderGeometry(
4755
+ layoutPlaceholderStyles,
4756
+ masterPlaceholderStyles
4757
+ );
4671
4758
  const slide = parseSlide(
4672
4759
  slideXml,
4673
4760
  path,
@@ -4675,11 +4762,10 @@ function parseSlideWithLayout(slideNumber, path, data) {
4675
4762
  data.archive,
4676
4763
  slideColorResolver,
4677
4764
  data.theme.fontScheme,
4678
- data.theme.fmtScheme
4765
+ data.theme.fmtScheme,
4766
+ mergedPlaceholderStyles
4679
4767
  );
4680
4768
  let layoutElements = [];
4681
- let layoutPlaceholderStyles = [];
4682
- let layoutShowMasterSp = true;
4683
4769
  if (layoutXml && layoutPath) {
4684
4770
  if (!slide.background) {
4685
4771
  const layoutFillContext = {
@@ -4701,13 +4787,10 @@ function parseSlideWithLayout(slideNumber, path, data) {
4701
4787
  data.theme.fontScheme,
4702
4788
  data.theme.fmtScheme
4703
4789
  );
4704
- layoutPlaceholderStyles = parseSlideLayoutPlaceholderStyles(layoutXml, slideColorResolver);
4705
- layoutShowMasterSp = parseSlideLayoutShowMasterSp(layoutXml);
4706
4790
  }
4707
4791
  if (!slide.background) {
4708
4792
  slide.background = slideMasterData?.background ?? data.masterBackground;
4709
4793
  }
4710
- const masterPlaceholderStyles = slideMasterData?.placeholderStyles ?? data.masterPlaceholderStyles;
4711
4794
  const masterTxStyles = slideMasterData?.txStyles ?? data.masterTxStyles;
4712
4795
  applyTextStyleInheritance(slide.elements, {
4713
4796
  layoutPlaceholderStyles,
@@ -4719,6 +4802,38 @@ function parseSlideWithLayout(slideNumber, path, data) {
4719
4802
  const masterElements = slideMasterData?.elements ?? data.masterElements;
4720
4803
  return { slide, layoutElements, layoutShowMasterSp, masterElements };
4721
4804
  }
4805
+ function mergePlaceholderGeometry(layoutStyles, masterStyles) {
4806
+ const merged = layoutStyles.map((ls) => {
4807
+ if (ls.transform) return ls;
4808
+ const masterMatch = findPlaceholderByTypeAndIdx(
4809
+ ls.placeholderType,
4810
+ ls.placeholderIdx,
4811
+ masterStyles
4812
+ );
4813
+ if (!masterMatch) return ls;
4814
+ return {
4815
+ ...ls,
4816
+ ...!ls.transform && masterMatch.transform && { transform: masterMatch.transform },
4817
+ ...!ls.geometry && masterMatch.geometry && { geometry: masterMatch.geometry }
4818
+ };
4819
+ });
4820
+ for (const ms of masterStyles) {
4821
+ const exists = merged.some(
4822
+ (m) => m.placeholderType === ms.placeholderType && m.placeholderIdx === ms.placeholderIdx
4823
+ );
4824
+ if (!exists) {
4825
+ merged.push(ms);
4826
+ }
4827
+ }
4828
+ return merged;
4829
+ }
4830
+ function findPlaceholderByTypeAndIdx(type, idx, styles) {
4831
+ if (idx !== void 0) {
4832
+ const byIdx = styles.find((s) => s.placeholderIdx === idx);
4833
+ if (byIdx) return byIdx;
4834
+ }
4835
+ return styles.find((s) => s.placeholderType === type);
4836
+ }
4722
4837
  function defaultColorScheme2() {
4723
4838
  return {
4724
4839
  dk1: "#000000",
@@ -8761,5 +8876,6 @@ export {
8761
8876
  createOpentypeTextMeasurerFromBuffers,
8762
8877
  getMappedFont,
8763
8878
  getWarningEntries,
8764
- getWarningSummary
8879
+ getWarningSummary,
8880
+ initResvgWasm
8765
8881
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pptx-glimpse",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
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",
@@ -64,7 +64,7 @@
64
64
  "author": "hirokisakabe",
65
65
  "license": "MIT",
66
66
  "dependencies": {
67
- "@resvg/resvg-js": "^2.6.2",
67
+ "@resvg/resvg-wasm": "^2.6.2",
68
68
  "fast-xml-parser": "^5.3.6",
69
69
  "fflate": "^0.8.2",
70
70
  "opentype.js": "^1.3.4"