pptx-glimpse 0.1.3 → 0.1.4

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
@@ -1144,6 +1144,14 @@ function buildMarkerDef(id, endpoint, color, alpha) {
1144
1144
  return `<marker id="${id}" markerWidth="${mw}" markerHeight="${mh}" refX="${mw}" refY="${mh / 2}" orient="auto" markerUnits="userSpaceOnUse"><path d="${path}" ${fillAttr2}${alphaAttr}/></marker>`;
1145
1145
  }
1146
1146
 
1147
+ // src/utils/unit-types.ts
1148
+ function asEmu(value) {
1149
+ return value;
1150
+ }
1151
+ function asHundredthPt(value) {
1152
+ return value;
1153
+ }
1154
+
1147
1155
  // src/data/font-metrics.ts
1148
1156
  var metricsData = {
1149
1157
  Carlito: {
@@ -3020,7 +3028,7 @@ function computeSpAutofitHeight(textBody, transform) {
3020
3028
  const requiredHeightPx = textHeight + marginTopPx + marginBottomPx;
3021
3029
  if (requiredHeightPx <= height) return null;
3022
3030
  const DEFAULT_DPI2 = 96;
3023
- return requiredHeightPx / DEFAULT_DPI2 * EMU_PER_INCH;
3031
+ return asEmu(requiredHeightPx / DEFAULT_DPI2 * EMU_PER_INCH);
3024
3032
  }
3025
3033
  function computeShrinkToFitScale(paragraphs, defaultFontSize, fontScale, lnSpcReduction, textWidth, availableHeight) {
3026
3034
  if (availableHeight <= 0) return fontScale;
@@ -4295,8 +4303,8 @@ function renderTable(element) {
4295
4303
  }
4296
4304
  if (cell.textBody) {
4297
4305
  const cellTransform = {
4298
- offsetX: 0,
4299
- offsetY: 0,
4306
+ offsetX: asEmu(0),
4307
+ offsetY: asEmu(0),
4300
4308
  extentWidth: pixelsToEmu(cellW),
4301
4309
  extentHeight: pixelsToEmu(cellH),
4302
4310
  rotation: 0,
@@ -4324,7 +4332,7 @@ function computeSpannedSize(sizes, startIdx, span) {
4324
4332
  return total;
4325
4333
  }
4326
4334
  function pixelsToEmu(px) {
4327
- return px / 96 * 914400;
4335
+ return asEmu(px / 96 * 914400);
4328
4336
  }
4329
4337
 
4330
4338
  // src/renderer/svg-renderer.ts
@@ -4628,7 +4636,7 @@ function parseDefaultRunProperties(defRPr, colorResolver) {
4628
4636
  if (!defRPr) return void 0;
4629
4637
  const result = {};
4630
4638
  if (defRPr["@_sz"] !== void 0) {
4631
- result.fontSize = hundredthPointToPoint(Number(defRPr["@_sz"]));
4639
+ result.fontSize = hundredthPointToPoint(asHundredthPt(Number(defRPr["@_sz"])));
4632
4640
  }
4633
4641
  const latin = defRPr.latin;
4634
4642
  if (latin?.["@_typeface"] !== void 0) {
@@ -4672,10 +4680,10 @@ function parseParagraphLevelProperties(node, colorResolver) {
4672
4680
  result.alignment = node["@_algn"];
4673
4681
  }
4674
4682
  if (node["@_marL"] !== void 0) {
4675
- result.marginLeft = Number(node["@_marL"]);
4683
+ result.marginLeft = asEmu(Number(node["@_marL"]));
4676
4684
  }
4677
4685
  if (node["@_indent"] !== void 0) {
4678
- result.indent = Number(node["@_indent"]);
4686
+ result.indent = asEmu(Number(node["@_indent"]));
4679
4687
  }
4680
4688
  const defRPr = parseDefaultRunProperties(node.defRPr, colorResolver);
4681
4689
  if (defRPr) {
@@ -4716,8 +4724,8 @@ function resolveThemeFont(typeface, fontScheme) {
4716
4724
  }
4717
4725
 
4718
4726
  // src/parser/presentation-parser.ts
4719
- var DEFAULT_SLIDE_WIDTH = 9144e3;
4720
- var DEFAULT_SLIDE_HEIGHT = 5143500;
4727
+ var DEFAULT_SLIDE_WIDTH = asEmu(9144e3);
4728
+ var DEFAULT_SLIDE_HEIGHT = asEmu(5143500);
4721
4729
  function parsePresentation(xml) {
4722
4730
  const parsed = parseXml(xml);
4723
4731
  const pres = parsed.presentation;
@@ -4738,8 +4746,8 @@ function parsePresentation(xml) {
4738
4746
  slideSize = { width: DEFAULT_SLIDE_WIDTH, height: DEFAULT_SLIDE_HEIGHT };
4739
4747
  } else {
4740
4748
  slideSize = {
4741
- width: Number(sldSz["@_cx"]),
4742
- height: Number(sldSz["@_cy"])
4749
+ width: asEmu(Number(sldSz["@_cx"])),
4750
+ height: asEmu(Number(sldSz["@_cy"]))
4743
4751
  };
4744
4752
  }
4745
4753
  const sldIdLst = pres.sldIdLst;
@@ -4897,8 +4905,8 @@ function parseBlipFill(blipFillNode, context) {
4897
4905
  const imageData = uint8ArrayToBase64(mediaData);
4898
4906
  const tileNode = blipFillNode.tile;
4899
4907
  const tile = tileNode ? {
4900
- tx: Number(tileNode["@_tx"] ?? 0),
4901
- ty: Number(tileNode["@_ty"] ?? 0),
4908
+ tx: asEmu(Number(tileNode["@_tx"] ?? 0)),
4909
+ ty: asEmu(Number(tileNode["@_ty"] ?? 0)),
4902
4910
  sx: Number(tileNode["@_sx"] ?? 1e5) / 1e5,
4903
4911
  sy: Number(tileNode["@_sy"] ?? 1e5) / 1e5,
4904
4912
  flip: tileNode["@_flip"] ?? "none",
@@ -4955,7 +4963,7 @@ function parseOutline(lnNode, colorResolver) {
4955
4963
  if (lnNode.pattFill) {
4956
4964
  warn("ln.pattFill", "pattern line fill not implemented");
4957
4965
  }
4958
- const width = Number(lnNode["@_w"] ?? 12700);
4966
+ const width = asEmu(Number(lnNode["@_w"] ?? 12700));
4959
4967
  let fill = null;
4960
4968
  if (lnNode.solidFill) {
4961
4969
  const color = colorResolver.resolve(lnNode.solidFill);
@@ -5036,8 +5044,8 @@ function parseOuterShadow(node, colorResolver) {
5036
5044
  const color = colorResolver.resolve(node);
5037
5045
  if (!color) return null;
5038
5046
  return {
5039
- blurRadius: Number(node["@_blurRad"] ?? 0),
5040
- distance: Number(node["@_dist"] ?? 0),
5047
+ blurRadius: asEmu(Number(node["@_blurRad"] ?? 0)),
5048
+ distance: asEmu(Number(node["@_dist"] ?? 0)),
5041
5049
  direction: Number(node["@_dir"] ?? 0) / 6e4,
5042
5050
  color,
5043
5051
  alignment: node["@_algn"] ?? "b",
@@ -5049,8 +5057,8 @@ function parseInnerShadow(node, colorResolver) {
5049
5057
  const color = colorResolver.resolve(node);
5050
5058
  if (!color) return null;
5051
5059
  return {
5052
- blurRadius: Number(node["@_blurRad"] ?? 0),
5053
- distance: Number(node["@_dist"] ?? 0),
5060
+ blurRadius: asEmu(Number(node["@_blurRad"] ?? 0)),
5061
+ distance: asEmu(Number(node["@_dist"] ?? 0)),
5054
5062
  direction: Number(node["@_dir"] ?? 0) / 6e4,
5055
5063
  color
5056
5064
  };
@@ -5060,14 +5068,14 @@ function parseGlow(node, colorResolver) {
5060
5068
  const color = colorResolver.resolve(node);
5061
5069
  if (!color) return null;
5062
5070
  return {
5063
- radius: Number(node["@_rad"] ?? 0),
5071
+ radius: asEmu(Number(node["@_rad"] ?? 0)),
5064
5072
  color
5065
5073
  };
5066
5074
  }
5067
5075
  function parseSoftEdge(node) {
5068
5076
  if (!node) return null;
5069
5077
  return {
5070
- radius: Number(node["@_rad"] ?? 0)
5078
+ radius: asEmu(Number(node["@_rad"] ?? 0))
5071
5079
  };
5072
5080
  }
5073
5081
 
@@ -5536,7 +5544,7 @@ function parseBiLevel(node) {
5536
5544
  function parseBlur(node) {
5537
5545
  if (!node) return null;
5538
5546
  return {
5539
- radius: Number(node["@_rad"] ?? 0),
5547
+ radius: asEmu(Number(node["@_rad"] ?? 0)),
5540
5548
  grow: node["@_grow"] !== "0"
5541
5549
  };
5542
5550
  }
@@ -6002,28 +6010,31 @@ function parseTable(tblNode, colorResolver, fontScheme) {
6002
6010
  if (!tblNode) return null;
6003
6011
  const columns = parseColumns(tblNode.tblGrid);
6004
6012
  if (columns.length === 0) return null;
6005
- const rows = parseRows(tblNode.tr, colorResolver, fontScheme);
6013
+ const tblPr = tblNode.tblPr;
6014
+ const hasTableStyle = tblPr?.tableStyleId !== void 0;
6015
+ const defaultBorders = hasTableStyle ? createDefaultBorders() : null;
6016
+ const rows = parseRows(tblNode.tr, colorResolver, fontScheme, defaultBorders);
6006
6017
  return { rows, columns };
6007
6018
  }
6008
6019
  function parseColumns(tblGrid) {
6009
6020
  if (!tblGrid) return [];
6010
6021
  const gridCols = tblGrid.gridCol ?? [];
6011
6022
  return gridCols.map((col) => ({
6012
- width: Number(col["@_w"] ?? 0)
6023
+ width: asEmu(Number(col["@_w"] ?? 0))
6013
6024
  }));
6014
6025
  }
6015
- function parseRows(trList, colorResolver, fontScheme) {
6026
+ function parseRows(trList, colorResolver, fontScheme, defaultBorders) {
6016
6027
  if (!trList) return [];
6017
6028
  const trArr = Array.isArray(trList) ? trList : [trList];
6018
6029
  const rows = [];
6019
6030
  for (const tr of trArr) {
6020
- const height = Number(tr["@_h"] ?? 0);
6021
- const cells = parseCells(tr.tc, colorResolver, fontScheme);
6031
+ const height = asEmu(Number(tr["@_h"] ?? 0));
6032
+ const cells = parseCells(tr.tc, colorResolver, fontScheme, defaultBorders);
6022
6033
  rows.push({ height, cells });
6023
6034
  }
6024
6035
  return rows;
6025
6036
  }
6026
- function parseCells(tcList, colorResolver, fontScheme) {
6037
+ function parseCells(tcList, colorResolver, fontScheme, defaultBorders) {
6027
6038
  if (!tcList) return [];
6028
6039
  const tcArr = Array.isArray(tcList) ? tcList : [tcList];
6029
6040
  const cells = [];
@@ -6031,7 +6042,8 @@ function parseCells(tcList, colorResolver, fontScheme) {
6031
6042
  const textBody = parseTextBody(tc.txBody, colorResolver, void 0, fontScheme);
6032
6043
  const tcPr = tc.tcPr;
6033
6044
  const fill = tcPr ? parseFillFromNode(tcPr, colorResolver) : null;
6034
- const borders = tcPr ? parseCellBorders(tcPr, colorResolver) : null;
6045
+ const inlineBorders = tcPr ? parseCellBorders(tcPr, colorResolver) : null;
6046
+ const borders = inlineBorders ?? defaultBorders ?? null;
6035
6047
  const gridSpan = Number(tc["@_gridSpan"] ?? 1);
6036
6048
  const rowSpan = Number(tc["@_rowSpan"] ?? 1);
6037
6049
  const hMerge = tcPr?.["@_hMerge"] === "1" || tcPr?.["@_hMerge"] === "true";
@@ -6045,9 +6057,29 @@ function parseCellBorders(tcPr, colorResolver) {
6045
6057
  const bottom = parseOutline(tcPr.lnB, colorResolver);
6046
6058
  const left = parseOutline(tcPr.lnL, colorResolver);
6047
6059
  const right = parseOutline(tcPr.lnR, colorResolver);
6060
+ for (const border of [top, bottom, left, right]) {
6061
+ if (border && !border.fill) {
6062
+ border.fill = { type: "solid", color: { hex: "#000000", alpha: 1 } };
6063
+ }
6064
+ }
6048
6065
  if (!top && !bottom && !left && !right) return null;
6049
6066
  return { top, bottom, left, right };
6050
6067
  }
6068
+ function createDefaultBorders() {
6069
+ const defaultOutline = {
6070
+ width: asEmu(12700),
6071
+ fill: { type: "solid", color: { hex: "#000000", alpha: 1 } },
6072
+ dashStyle: "solid",
6073
+ headEnd: null,
6074
+ tailEnd: null
6075
+ };
6076
+ return {
6077
+ top: { ...defaultOutline },
6078
+ bottom: { ...defaultOutline },
6079
+ left: { ...defaultOutline },
6080
+ right: { ...defaultOutline }
6081
+ };
6082
+ }
6051
6083
 
6052
6084
  // src/parser/slide-parser.ts
6053
6085
  var SHAPE_TAGS = /* @__PURE__ */ new Set(["sp", "pic", "cxnSp", "grpSp", "graphicFrame"]);
@@ -6475,8 +6507,8 @@ function parseStretchFillRect(stretchNode) {
6475
6507
  function parseTileInfo(node) {
6476
6508
  if (!node) return null;
6477
6509
  return {
6478
- tx: Number(node["@_tx"] ?? 0),
6479
- ty: Number(node["@_ty"] ?? 0),
6510
+ tx: asEmu(Number(node["@_tx"] ?? 0)),
6511
+ ty: asEmu(Number(node["@_ty"] ?? 0)),
6480
6512
  sx: Number(node["@_sx"] ?? 1e5) / 1e5,
6481
6513
  sy: Number(node["@_sy"] ?? 1e5) / 1e5,
6482
6514
  flip: node["@_flip"] ?? "none",
@@ -6508,10 +6540,10 @@ function parseGroup(grp, rels, slidePath, archive, colorResolver, parentFillCont
6508
6540
  const childOff = xfrm?.chOff;
6509
6541
  const childExt = xfrm?.chExt;
6510
6542
  const childTransform = {
6511
- offsetX: Number(childOff?.["@_x"] ?? 0),
6512
- offsetY: Number(childOff?.["@_y"] ?? 0),
6513
- extentWidth: Number(childExt?.["@_cx"] ?? transform.extentWidth),
6514
- extentHeight: Number(childExt?.["@_cy"] ?? transform.extentHeight),
6543
+ offsetX: asEmu(Number(childOff?.["@_x"] ?? 0)),
6544
+ offsetY: asEmu(Number(childOff?.["@_y"] ?? 0)),
6545
+ extentWidth: asEmu(Number(childExt?.["@_cx"] ?? transform.extentWidth)),
6546
+ extentHeight: asEmu(Number(childExt?.["@_cy"] ?? transform.extentHeight)),
6515
6547
  rotation: 0,
6516
6548
  flipH: false,
6517
6549
  flipV: false
@@ -6632,10 +6664,10 @@ function parseSmartArt(graphicData, transform, rels, slidePath, archive, colorRe
6632
6664
  const childOff = grpXfrm?.chOff;
6633
6665
  const childExt = grpXfrm?.chExt;
6634
6666
  const childTransform = {
6635
- offsetX: Number(childOff?.["@_x"] ?? 0),
6636
- offsetY: Number(childOff?.["@_y"] ?? 0),
6637
- extentWidth: Number(childExt?.["@_cx"] ?? transform.extentWidth),
6638
- extentHeight: Number(childExt?.["@_cy"] ?? transform.extentHeight),
6667
+ offsetX: asEmu(Number(childOff?.["@_x"] ?? 0)),
6668
+ offsetY: asEmu(Number(childOff?.["@_y"] ?? 0)),
6669
+ extentWidth: asEmu(Number(childExt?.["@_cx"] ?? transform.extentWidth)),
6670
+ extentHeight: asEmu(Number(childExt?.["@_cy"] ?? transform.extentHeight)),
6639
6671
  rotation: 0,
6640
6672
  flipH: false,
6641
6673
  flipV: false
@@ -6672,26 +6704,26 @@ function parseTransform(xfrm) {
6672
6704
  const off = xfrm.off;
6673
6705
  const ext = xfrm.ext;
6674
6706
  if (!off || !ext) return null;
6675
- let offsetX = Number(off["@_x"] ?? 0);
6676
- let offsetY = Number(off["@_y"] ?? 0);
6677
- let extentWidth = Number(ext["@_cx"] ?? 0);
6678
- let extentHeight = Number(ext["@_cy"] ?? 0);
6707
+ let offsetX = asEmu(Number(off["@_x"] ?? 0));
6708
+ let offsetY = asEmu(Number(off["@_y"] ?? 0));
6709
+ let extentWidth = asEmu(Number(ext["@_cx"] ?? 0));
6710
+ let extentHeight = asEmu(Number(ext["@_cy"] ?? 0));
6679
6711
  let rotation = Number(xfrm["@_rot"] ?? 0);
6680
6712
  if (Number.isNaN(offsetX)) {
6681
6713
  debug("transform.nan", "NaN detected in transform offsetX, defaulting to 0");
6682
- offsetX = 0;
6714
+ offsetX = asEmu(0);
6683
6715
  }
6684
6716
  if (Number.isNaN(offsetY)) {
6685
6717
  debug("transform.nan", "NaN detected in transform offsetY, defaulting to 0");
6686
- offsetY = 0;
6718
+ offsetY = asEmu(0);
6687
6719
  }
6688
6720
  if (Number.isNaN(extentWidth)) {
6689
6721
  debug("transform.nan", "NaN detected in transform extentWidth, defaulting to 0");
6690
- extentWidth = 0;
6722
+ extentWidth = asEmu(0);
6691
6723
  }
6692
6724
  if (Number.isNaN(extentHeight)) {
6693
6725
  debug("transform.nan", "NaN detected in transform extentHeight, defaulting to 0");
6694
- extentHeight = 0;
6726
+ extentHeight = asEmu(0);
6695
6727
  }
6696
6728
  if (Number.isNaN(rotation)) {
6697
6729
  debug("transform.nan", "NaN detected in transform rotation, defaulting to 0");
@@ -6756,10 +6788,10 @@ function parseTextBody(txBody, colorResolver, rels, fontScheme, lstStyleOverride
6756
6788
  }
6757
6789
  const bodyProperties = {
6758
6790
  anchor: bodyPr?.["@_anchor"] ?? "t",
6759
- marginLeft: Number(bodyPr?.["@_lIns"] ?? 91440),
6760
- marginRight: Number(bodyPr?.["@_rIns"] ?? 91440),
6761
- marginTop: Number(bodyPr?.["@_tIns"] ?? 45720),
6762
- marginBottom: Number(bodyPr?.["@_bIns"] ?? 45720),
6791
+ marginLeft: asEmu(Number(bodyPr?.["@_lIns"] ?? 91440)),
6792
+ marginRight: asEmu(Number(bodyPr?.["@_rIns"] ?? 91440)),
6793
+ marginTop: asEmu(Number(bodyPr?.["@_tIns"] ?? 45720)),
6794
+ marginBottom: asEmu(Number(bodyPr?.["@_bIns"] ?? 45720)),
6763
6795
  wrap: bodyPr?.["@_wrap"] ?? "square",
6764
6796
  autoFit,
6765
6797
  fontScale,
@@ -6828,13 +6860,13 @@ function parseBullet(pPr, colorResolver) {
6828
6860
  function parseSpacing(spc) {
6829
6861
  if (spc?.spcPts) {
6830
6862
  const spcPts = spc.spcPts;
6831
- return { type: "pts", value: Number(spcPts["@_val"]) };
6863
+ return { type: "pts", value: asHundredthPt(Number(spcPts["@_val"])) };
6832
6864
  }
6833
6865
  if (spc?.spcPct) {
6834
6866
  const spcPct = spc.spcPct;
6835
6867
  return { type: "pct", value: Number(spcPct["@_val"]) };
6836
6868
  }
6837
- return { type: "pts", value: 0 };
6869
+ return { type: "pts", value: asHundredthPt(0) };
6838
6870
  }
6839
6871
  function parseTabStops(pPr) {
6840
6872
  const tabLst = pPr?.tabLst;
@@ -6843,7 +6875,7 @@ function parseTabStops(pPr) {
6843
6875
  if (!tabs) return [];
6844
6876
  const tabArr = Array.isArray(tabs) ? tabs : [tabs];
6845
6877
  return tabArr.map((tab) => ({
6846
- position: Number(tab["@_pos"] ?? 0),
6878
+ position: asEmu(Number(tab["@_pos"] ?? 0)),
6847
6879
  alignment: tab["@_algn"] ?? "l"
6848
6880
  }));
6849
6881
  }
@@ -6893,8 +6925,8 @@ function parseParagraph(p, colorResolver, rels, fontScheme, lstStyle, orderedPCh
6893
6925
  bulletFont,
6894
6926
  bulletColor,
6895
6927
  bulletSizePct,
6896
- marginLeft: pPr?.["@_marL"] !== void 0 ? Number(pPr["@_marL"]) : lstLevelProps?.marginLeft ?? 0,
6897
- indent: pPr?.["@_indent"] !== void 0 ? Number(pPr["@_indent"]) : lstLevelProps?.indent ?? 0,
6928
+ marginLeft: pPr?.["@_marL"] !== void 0 ? asEmu(Number(pPr["@_marL"])) : lstLevelProps?.marginLeft ?? asEmu(0),
6929
+ indent: pPr?.["@_indent"] !== void 0 ? asEmu(Number(pPr["@_indent"])) : lstLevelProps?.indent ?? asEmu(0),
6898
6930
  tabStops
6899
6931
  };
6900
6932
  const pPrDefRPr = parseDefaultRunProperties(pPr?.defRPr);
@@ -7036,7 +7068,7 @@ function parseRunProperties(rPr, colorResolver, rels, fontScheme, defaults) {
7036
7068
  const latin = rPr.latin;
7037
7069
  const ea = rPr.ea;
7038
7070
  const cs = rPr.cs;
7039
- const fontSize = rPr["@_sz"] ? hundredthPointToPoint(Number(rPr["@_sz"])) : defaults?.fontSize ?? null;
7071
+ const fontSize = rPr["@_sz"] ? hundredthPointToPoint(asHundredthPt(Number(rPr["@_sz"]))) : defaults?.fontSize ?? null;
7040
7072
  const fontFamily = resolveThemeFont(
7041
7073
  latin?.["@_typeface"] ?? defaults?.fontFamily ?? null,
7042
7074
  fontScheme
@@ -7063,7 +7095,7 @@ function parseRunProperties(rPr, colorResolver, rels, fontScheme, defaults) {
7063
7095
  const ln = rPr.ln;
7064
7096
  let outline = null;
7065
7097
  if (ln) {
7066
- const lnWidth = Number(ln["@_w"] ?? 12700);
7098
+ const lnWidth = asEmu(Number(ln["@_w"] ?? 12700));
7067
7099
  const lnFill = ln.solidFill;
7068
7100
  const lnColor = lnFill ? colorResolver.resolve(lnFill) : null;
7069
7101
  if (lnColor) {
package/dist/index.js CHANGED
@@ -1101,6 +1101,14 @@ function buildMarkerDef(id, endpoint, color, alpha) {
1101
1101
  return `<marker id="${id}" markerWidth="${mw}" markerHeight="${mh}" refX="${mw}" refY="${mh / 2}" orient="auto" markerUnits="userSpaceOnUse"><path d="${path}" ${fillAttr2}${alphaAttr}/></marker>`;
1102
1102
  }
1103
1103
 
1104
+ // src/utils/unit-types.ts
1105
+ function asEmu(value) {
1106
+ return value;
1107
+ }
1108
+ function asHundredthPt(value) {
1109
+ return value;
1110
+ }
1111
+
1104
1112
  // src/data/font-metrics.ts
1105
1113
  var metricsData = {
1106
1114
  Carlito: {
@@ -2977,7 +2985,7 @@ function computeSpAutofitHeight(textBody, transform) {
2977
2985
  const requiredHeightPx = textHeight + marginTopPx + marginBottomPx;
2978
2986
  if (requiredHeightPx <= height) return null;
2979
2987
  const DEFAULT_DPI2 = 96;
2980
- return requiredHeightPx / DEFAULT_DPI2 * EMU_PER_INCH;
2988
+ return asEmu(requiredHeightPx / DEFAULT_DPI2 * EMU_PER_INCH);
2981
2989
  }
2982
2990
  function computeShrinkToFitScale(paragraphs, defaultFontSize, fontScale, lnSpcReduction, textWidth, availableHeight) {
2983
2991
  if (availableHeight <= 0) return fontScale;
@@ -4252,8 +4260,8 @@ function renderTable(element) {
4252
4260
  }
4253
4261
  if (cell.textBody) {
4254
4262
  const cellTransform = {
4255
- offsetX: 0,
4256
- offsetY: 0,
4263
+ offsetX: asEmu(0),
4264
+ offsetY: asEmu(0),
4257
4265
  extentWidth: pixelsToEmu(cellW),
4258
4266
  extentHeight: pixelsToEmu(cellH),
4259
4267
  rotation: 0,
@@ -4281,7 +4289,7 @@ function computeSpannedSize(sizes, startIdx, span) {
4281
4289
  return total;
4282
4290
  }
4283
4291
  function pixelsToEmu(px) {
4284
- return px / 96 * 914400;
4292
+ return asEmu(px / 96 * 914400);
4285
4293
  }
4286
4294
 
4287
4295
  // src/renderer/svg-renderer.ts
@@ -4585,7 +4593,7 @@ function parseDefaultRunProperties(defRPr, colorResolver) {
4585
4593
  if (!defRPr) return void 0;
4586
4594
  const result = {};
4587
4595
  if (defRPr["@_sz"] !== void 0) {
4588
- result.fontSize = hundredthPointToPoint(Number(defRPr["@_sz"]));
4596
+ result.fontSize = hundredthPointToPoint(asHundredthPt(Number(defRPr["@_sz"])));
4589
4597
  }
4590
4598
  const latin = defRPr.latin;
4591
4599
  if (latin?.["@_typeface"] !== void 0) {
@@ -4629,10 +4637,10 @@ function parseParagraphLevelProperties(node, colorResolver) {
4629
4637
  result.alignment = node["@_algn"];
4630
4638
  }
4631
4639
  if (node["@_marL"] !== void 0) {
4632
- result.marginLeft = Number(node["@_marL"]);
4640
+ result.marginLeft = asEmu(Number(node["@_marL"]));
4633
4641
  }
4634
4642
  if (node["@_indent"] !== void 0) {
4635
- result.indent = Number(node["@_indent"]);
4643
+ result.indent = asEmu(Number(node["@_indent"]));
4636
4644
  }
4637
4645
  const defRPr = parseDefaultRunProperties(node.defRPr, colorResolver);
4638
4646
  if (defRPr) {
@@ -4673,8 +4681,8 @@ function resolveThemeFont(typeface, fontScheme) {
4673
4681
  }
4674
4682
 
4675
4683
  // src/parser/presentation-parser.ts
4676
- var DEFAULT_SLIDE_WIDTH = 9144e3;
4677
- var DEFAULT_SLIDE_HEIGHT = 5143500;
4684
+ var DEFAULT_SLIDE_WIDTH = asEmu(9144e3);
4685
+ var DEFAULT_SLIDE_HEIGHT = asEmu(5143500);
4678
4686
  function parsePresentation(xml) {
4679
4687
  const parsed = parseXml(xml);
4680
4688
  const pres = parsed.presentation;
@@ -4695,8 +4703,8 @@ function parsePresentation(xml) {
4695
4703
  slideSize = { width: DEFAULT_SLIDE_WIDTH, height: DEFAULT_SLIDE_HEIGHT };
4696
4704
  } else {
4697
4705
  slideSize = {
4698
- width: Number(sldSz["@_cx"]),
4699
- height: Number(sldSz["@_cy"])
4706
+ width: asEmu(Number(sldSz["@_cx"])),
4707
+ height: asEmu(Number(sldSz["@_cy"]))
4700
4708
  };
4701
4709
  }
4702
4710
  const sldIdLst = pres.sldIdLst;
@@ -4854,8 +4862,8 @@ function parseBlipFill(blipFillNode, context) {
4854
4862
  const imageData = uint8ArrayToBase64(mediaData);
4855
4863
  const tileNode = blipFillNode.tile;
4856
4864
  const tile = tileNode ? {
4857
- tx: Number(tileNode["@_tx"] ?? 0),
4858
- ty: Number(tileNode["@_ty"] ?? 0),
4865
+ tx: asEmu(Number(tileNode["@_tx"] ?? 0)),
4866
+ ty: asEmu(Number(tileNode["@_ty"] ?? 0)),
4859
4867
  sx: Number(tileNode["@_sx"] ?? 1e5) / 1e5,
4860
4868
  sy: Number(tileNode["@_sy"] ?? 1e5) / 1e5,
4861
4869
  flip: tileNode["@_flip"] ?? "none",
@@ -4912,7 +4920,7 @@ function parseOutline(lnNode, colorResolver) {
4912
4920
  if (lnNode.pattFill) {
4913
4921
  warn("ln.pattFill", "pattern line fill not implemented");
4914
4922
  }
4915
- const width = Number(lnNode["@_w"] ?? 12700);
4923
+ const width = asEmu(Number(lnNode["@_w"] ?? 12700));
4916
4924
  let fill = null;
4917
4925
  if (lnNode.solidFill) {
4918
4926
  const color = colorResolver.resolve(lnNode.solidFill);
@@ -4993,8 +5001,8 @@ function parseOuterShadow(node, colorResolver) {
4993
5001
  const color = colorResolver.resolve(node);
4994
5002
  if (!color) return null;
4995
5003
  return {
4996
- blurRadius: Number(node["@_blurRad"] ?? 0),
4997
- distance: Number(node["@_dist"] ?? 0),
5004
+ blurRadius: asEmu(Number(node["@_blurRad"] ?? 0)),
5005
+ distance: asEmu(Number(node["@_dist"] ?? 0)),
4998
5006
  direction: Number(node["@_dir"] ?? 0) / 6e4,
4999
5007
  color,
5000
5008
  alignment: node["@_algn"] ?? "b",
@@ -5006,8 +5014,8 @@ function parseInnerShadow(node, colorResolver) {
5006
5014
  const color = colorResolver.resolve(node);
5007
5015
  if (!color) return null;
5008
5016
  return {
5009
- blurRadius: Number(node["@_blurRad"] ?? 0),
5010
- distance: Number(node["@_dist"] ?? 0),
5017
+ blurRadius: asEmu(Number(node["@_blurRad"] ?? 0)),
5018
+ distance: asEmu(Number(node["@_dist"] ?? 0)),
5011
5019
  direction: Number(node["@_dir"] ?? 0) / 6e4,
5012
5020
  color
5013
5021
  };
@@ -5017,14 +5025,14 @@ function parseGlow(node, colorResolver) {
5017
5025
  const color = colorResolver.resolve(node);
5018
5026
  if (!color) return null;
5019
5027
  return {
5020
- radius: Number(node["@_rad"] ?? 0),
5028
+ radius: asEmu(Number(node["@_rad"] ?? 0)),
5021
5029
  color
5022
5030
  };
5023
5031
  }
5024
5032
  function parseSoftEdge(node) {
5025
5033
  if (!node) return null;
5026
5034
  return {
5027
- radius: Number(node["@_rad"] ?? 0)
5035
+ radius: asEmu(Number(node["@_rad"] ?? 0))
5028
5036
  };
5029
5037
  }
5030
5038
 
@@ -5493,7 +5501,7 @@ function parseBiLevel(node) {
5493
5501
  function parseBlur(node) {
5494
5502
  if (!node) return null;
5495
5503
  return {
5496
- radius: Number(node["@_rad"] ?? 0),
5504
+ radius: asEmu(Number(node["@_rad"] ?? 0)),
5497
5505
  grow: node["@_grow"] !== "0"
5498
5506
  };
5499
5507
  }
@@ -5959,28 +5967,31 @@ function parseTable(tblNode, colorResolver, fontScheme) {
5959
5967
  if (!tblNode) return null;
5960
5968
  const columns = parseColumns(tblNode.tblGrid);
5961
5969
  if (columns.length === 0) return null;
5962
- const rows = parseRows(tblNode.tr, colorResolver, fontScheme);
5970
+ const tblPr = tblNode.tblPr;
5971
+ const hasTableStyle = tblPr?.tableStyleId !== void 0;
5972
+ const defaultBorders = hasTableStyle ? createDefaultBorders() : null;
5973
+ const rows = parseRows(tblNode.tr, colorResolver, fontScheme, defaultBorders);
5963
5974
  return { rows, columns };
5964
5975
  }
5965
5976
  function parseColumns(tblGrid) {
5966
5977
  if (!tblGrid) return [];
5967
5978
  const gridCols = tblGrid.gridCol ?? [];
5968
5979
  return gridCols.map((col) => ({
5969
- width: Number(col["@_w"] ?? 0)
5980
+ width: asEmu(Number(col["@_w"] ?? 0))
5970
5981
  }));
5971
5982
  }
5972
- function parseRows(trList, colorResolver, fontScheme) {
5983
+ function parseRows(trList, colorResolver, fontScheme, defaultBorders) {
5973
5984
  if (!trList) return [];
5974
5985
  const trArr = Array.isArray(trList) ? trList : [trList];
5975
5986
  const rows = [];
5976
5987
  for (const tr of trArr) {
5977
- const height = Number(tr["@_h"] ?? 0);
5978
- const cells = parseCells(tr.tc, colorResolver, fontScheme);
5988
+ const height = asEmu(Number(tr["@_h"] ?? 0));
5989
+ const cells = parseCells(tr.tc, colorResolver, fontScheme, defaultBorders);
5979
5990
  rows.push({ height, cells });
5980
5991
  }
5981
5992
  return rows;
5982
5993
  }
5983
- function parseCells(tcList, colorResolver, fontScheme) {
5994
+ function parseCells(tcList, colorResolver, fontScheme, defaultBorders) {
5984
5995
  if (!tcList) return [];
5985
5996
  const tcArr = Array.isArray(tcList) ? tcList : [tcList];
5986
5997
  const cells = [];
@@ -5988,7 +5999,8 @@ function parseCells(tcList, colorResolver, fontScheme) {
5988
5999
  const textBody = parseTextBody(tc.txBody, colorResolver, void 0, fontScheme);
5989
6000
  const tcPr = tc.tcPr;
5990
6001
  const fill = tcPr ? parseFillFromNode(tcPr, colorResolver) : null;
5991
- const borders = tcPr ? parseCellBorders(tcPr, colorResolver) : null;
6002
+ const inlineBorders = tcPr ? parseCellBorders(tcPr, colorResolver) : null;
6003
+ const borders = inlineBorders ?? defaultBorders ?? null;
5992
6004
  const gridSpan = Number(tc["@_gridSpan"] ?? 1);
5993
6005
  const rowSpan = Number(tc["@_rowSpan"] ?? 1);
5994
6006
  const hMerge = tcPr?.["@_hMerge"] === "1" || tcPr?.["@_hMerge"] === "true";
@@ -6002,9 +6014,29 @@ function parseCellBorders(tcPr, colorResolver) {
6002
6014
  const bottom = parseOutline(tcPr.lnB, colorResolver);
6003
6015
  const left = parseOutline(tcPr.lnL, colorResolver);
6004
6016
  const right = parseOutline(tcPr.lnR, colorResolver);
6017
+ for (const border of [top, bottom, left, right]) {
6018
+ if (border && !border.fill) {
6019
+ border.fill = { type: "solid", color: { hex: "#000000", alpha: 1 } };
6020
+ }
6021
+ }
6005
6022
  if (!top && !bottom && !left && !right) return null;
6006
6023
  return { top, bottom, left, right };
6007
6024
  }
6025
+ function createDefaultBorders() {
6026
+ const defaultOutline = {
6027
+ width: asEmu(12700),
6028
+ fill: { type: "solid", color: { hex: "#000000", alpha: 1 } },
6029
+ dashStyle: "solid",
6030
+ headEnd: null,
6031
+ tailEnd: null
6032
+ };
6033
+ return {
6034
+ top: { ...defaultOutline },
6035
+ bottom: { ...defaultOutline },
6036
+ left: { ...defaultOutline },
6037
+ right: { ...defaultOutline }
6038
+ };
6039
+ }
6008
6040
 
6009
6041
  // src/parser/slide-parser.ts
6010
6042
  var SHAPE_TAGS = /* @__PURE__ */ new Set(["sp", "pic", "cxnSp", "grpSp", "graphicFrame"]);
@@ -6432,8 +6464,8 @@ function parseStretchFillRect(stretchNode) {
6432
6464
  function parseTileInfo(node) {
6433
6465
  if (!node) return null;
6434
6466
  return {
6435
- tx: Number(node["@_tx"] ?? 0),
6436
- ty: Number(node["@_ty"] ?? 0),
6467
+ tx: asEmu(Number(node["@_tx"] ?? 0)),
6468
+ ty: asEmu(Number(node["@_ty"] ?? 0)),
6437
6469
  sx: Number(node["@_sx"] ?? 1e5) / 1e5,
6438
6470
  sy: Number(node["@_sy"] ?? 1e5) / 1e5,
6439
6471
  flip: node["@_flip"] ?? "none",
@@ -6465,10 +6497,10 @@ function parseGroup(grp, rels, slidePath, archive, colorResolver, parentFillCont
6465
6497
  const childOff = xfrm?.chOff;
6466
6498
  const childExt = xfrm?.chExt;
6467
6499
  const childTransform = {
6468
- offsetX: Number(childOff?.["@_x"] ?? 0),
6469
- offsetY: Number(childOff?.["@_y"] ?? 0),
6470
- extentWidth: Number(childExt?.["@_cx"] ?? transform.extentWidth),
6471
- extentHeight: Number(childExt?.["@_cy"] ?? transform.extentHeight),
6500
+ offsetX: asEmu(Number(childOff?.["@_x"] ?? 0)),
6501
+ offsetY: asEmu(Number(childOff?.["@_y"] ?? 0)),
6502
+ extentWidth: asEmu(Number(childExt?.["@_cx"] ?? transform.extentWidth)),
6503
+ extentHeight: asEmu(Number(childExt?.["@_cy"] ?? transform.extentHeight)),
6472
6504
  rotation: 0,
6473
6505
  flipH: false,
6474
6506
  flipV: false
@@ -6589,10 +6621,10 @@ function parseSmartArt(graphicData, transform, rels, slidePath, archive, colorRe
6589
6621
  const childOff = grpXfrm?.chOff;
6590
6622
  const childExt = grpXfrm?.chExt;
6591
6623
  const childTransform = {
6592
- offsetX: Number(childOff?.["@_x"] ?? 0),
6593
- offsetY: Number(childOff?.["@_y"] ?? 0),
6594
- extentWidth: Number(childExt?.["@_cx"] ?? transform.extentWidth),
6595
- extentHeight: Number(childExt?.["@_cy"] ?? transform.extentHeight),
6624
+ offsetX: asEmu(Number(childOff?.["@_x"] ?? 0)),
6625
+ offsetY: asEmu(Number(childOff?.["@_y"] ?? 0)),
6626
+ extentWidth: asEmu(Number(childExt?.["@_cx"] ?? transform.extentWidth)),
6627
+ extentHeight: asEmu(Number(childExt?.["@_cy"] ?? transform.extentHeight)),
6596
6628
  rotation: 0,
6597
6629
  flipH: false,
6598
6630
  flipV: false
@@ -6629,26 +6661,26 @@ function parseTransform(xfrm) {
6629
6661
  const off = xfrm.off;
6630
6662
  const ext = xfrm.ext;
6631
6663
  if (!off || !ext) return null;
6632
- let offsetX = Number(off["@_x"] ?? 0);
6633
- let offsetY = Number(off["@_y"] ?? 0);
6634
- let extentWidth = Number(ext["@_cx"] ?? 0);
6635
- let extentHeight = Number(ext["@_cy"] ?? 0);
6664
+ let offsetX = asEmu(Number(off["@_x"] ?? 0));
6665
+ let offsetY = asEmu(Number(off["@_y"] ?? 0));
6666
+ let extentWidth = asEmu(Number(ext["@_cx"] ?? 0));
6667
+ let extentHeight = asEmu(Number(ext["@_cy"] ?? 0));
6636
6668
  let rotation = Number(xfrm["@_rot"] ?? 0);
6637
6669
  if (Number.isNaN(offsetX)) {
6638
6670
  debug("transform.nan", "NaN detected in transform offsetX, defaulting to 0");
6639
- offsetX = 0;
6671
+ offsetX = asEmu(0);
6640
6672
  }
6641
6673
  if (Number.isNaN(offsetY)) {
6642
6674
  debug("transform.nan", "NaN detected in transform offsetY, defaulting to 0");
6643
- offsetY = 0;
6675
+ offsetY = asEmu(0);
6644
6676
  }
6645
6677
  if (Number.isNaN(extentWidth)) {
6646
6678
  debug("transform.nan", "NaN detected in transform extentWidth, defaulting to 0");
6647
- extentWidth = 0;
6679
+ extentWidth = asEmu(0);
6648
6680
  }
6649
6681
  if (Number.isNaN(extentHeight)) {
6650
6682
  debug("transform.nan", "NaN detected in transform extentHeight, defaulting to 0");
6651
- extentHeight = 0;
6683
+ extentHeight = asEmu(0);
6652
6684
  }
6653
6685
  if (Number.isNaN(rotation)) {
6654
6686
  debug("transform.nan", "NaN detected in transform rotation, defaulting to 0");
@@ -6713,10 +6745,10 @@ function parseTextBody(txBody, colorResolver, rels, fontScheme, lstStyleOverride
6713
6745
  }
6714
6746
  const bodyProperties = {
6715
6747
  anchor: bodyPr?.["@_anchor"] ?? "t",
6716
- marginLeft: Number(bodyPr?.["@_lIns"] ?? 91440),
6717
- marginRight: Number(bodyPr?.["@_rIns"] ?? 91440),
6718
- marginTop: Number(bodyPr?.["@_tIns"] ?? 45720),
6719
- marginBottom: Number(bodyPr?.["@_bIns"] ?? 45720),
6748
+ marginLeft: asEmu(Number(bodyPr?.["@_lIns"] ?? 91440)),
6749
+ marginRight: asEmu(Number(bodyPr?.["@_rIns"] ?? 91440)),
6750
+ marginTop: asEmu(Number(bodyPr?.["@_tIns"] ?? 45720)),
6751
+ marginBottom: asEmu(Number(bodyPr?.["@_bIns"] ?? 45720)),
6720
6752
  wrap: bodyPr?.["@_wrap"] ?? "square",
6721
6753
  autoFit,
6722
6754
  fontScale,
@@ -6785,13 +6817,13 @@ function parseBullet(pPr, colorResolver) {
6785
6817
  function parseSpacing(spc) {
6786
6818
  if (spc?.spcPts) {
6787
6819
  const spcPts = spc.spcPts;
6788
- return { type: "pts", value: Number(spcPts["@_val"]) };
6820
+ return { type: "pts", value: asHundredthPt(Number(spcPts["@_val"])) };
6789
6821
  }
6790
6822
  if (spc?.spcPct) {
6791
6823
  const spcPct = spc.spcPct;
6792
6824
  return { type: "pct", value: Number(spcPct["@_val"]) };
6793
6825
  }
6794
- return { type: "pts", value: 0 };
6826
+ return { type: "pts", value: asHundredthPt(0) };
6795
6827
  }
6796
6828
  function parseTabStops(pPr) {
6797
6829
  const tabLst = pPr?.tabLst;
@@ -6800,7 +6832,7 @@ function parseTabStops(pPr) {
6800
6832
  if (!tabs) return [];
6801
6833
  const tabArr = Array.isArray(tabs) ? tabs : [tabs];
6802
6834
  return tabArr.map((tab) => ({
6803
- position: Number(tab["@_pos"] ?? 0),
6835
+ position: asEmu(Number(tab["@_pos"] ?? 0)),
6804
6836
  alignment: tab["@_algn"] ?? "l"
6805
6837
  }));
6806
6838
  }
@@ -6850,8 +6882,8 @@ function parseParagraph(p, colorResolver, rels, fontScheme, lstStyle, orderedPCh
6850
6882
  bulletFont,
6851
6883
  bulletColor,
6852
6884
  bulletSizePct,
6853
- marginLeft: pPr?.["@_marL"] !== void 0 ? Number(pPr["@_marL"]) : lstLevelProps?.marginLeft ?? 0,
6854
- indent: pPr?.["@_indent"] !== void 0 ? Number(pPr["@_indent"]) : lstLevelProps?.indent ?? 0,
6885
+ marginLeft: pPr?.["@_marL"] !== void 0 ? asEmu(Number(pPr["@_marL"])) : lstLevelProps?.marginLeft ?? asEmu(0),
6886
+ indent: pPr?.["@_indent"] !== void 0 ? asEmu(Number(pPr["@_indent"])) : lstLevelProps?.indent ?? asEmu(0),
6855
6887
  tabStops
6856
6888
  };
6857
6889
  const pPrDefRPr = parseDefaultRunProperties(pPr?.defRPr);
@@ -6993,7 +7025,7 @@ function parseRunProperties(rPr, colorResolver, rels, fontScheme, defaults) {
6993
7025
  const latin = rPr.latin;
6994
7026
  const ea = rPr.ea;
6995
7027
  const cs = rPr.cs;
6996
- const fontSize = rPr["@_sz"] ? hundredthPointToPoint(Number(rPr["@_sz"])) : defaults?.fontSize ?? null;
7028
+ const fontSize = rPr["@_sz"] ? hundredthPointToPoint(asHundredthPt(Number(rPr["@_sz"]))) : defaults?.fontSize ?? null;
6997
7029
  const fontFamily = resolveThemeFont(
6998
7030
  latin?.["@_typeface"] ?? defaults?.fontFamily ?? null,
6999
7031
  fontScheme
@@ -7020,7 +7052,7 @@ function parseRunProperties(rPr, colorResolver, rels, fontScheme, defaults) {
7020
7052
  const ln = rPr.ln;
7021
7053
  let outline = null;
7022
7054
  if (ln) {
7023
- const lnWidth = Number(ln["@_w"] ?? 12700);
7055
+ const lnWidth = asEmu(Number(ln["@_w"] ?? 12700));
7024
7056
  const lnFill = ln.solidFill;
7025
7057
  const lnColor = lnFill ? colorResolver.resolve(lnFill) : null;
7026
7058
  if (lnColor) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pptx-glimpse",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "A Node.js library to render PPTX as SVG / PNG.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -16,10 +16,10 @@
16
16
  "scripts": {
17
17
  "build": "tsup src/index.ts --format cjs,esm --dts",
18
18
  "bench": "vitest bench",
19
- "lint": "eslint src/ vrt/ scripts/ bench/",
20
- "lint:fix": "eslint src/ vrt/ scripts/ bench/ --fix",
21
- "format": "prettier --write 'src/**/*.ts' 'vrt/**/*.ts' 'scripts/**/*.ts' 'bench/**/*.ts'",
22
- "format:check": "prettier --check 'src/**/*.ts' 'vrt/**/*.ts' 'scripts/**/*.ts' 'bench/**/*.ts'",
19
+ "lint": "eslint src/ vrt/ scripts/ bench/ e2e/",
20
+ "lint:fix": "eslint src/ vrt/ scripts/ bench/ e2e/ --fix",
21
+ "format": "prettier --write 'src/**/*.ts' 'vrt/**/*.ts' 'scripts/**/*.ts' 'bench/**/*.ts' 'e2e/**/*.ts'",
22
+ "format:check": "prettier --check 'src/**/*.ts' 'vrt/**/*.ts' 'scripts/**/*.ts' 'bench/**/*.ts' 'e2e/**/*.ts'",
23
23
  "test": "vitest run",
24
24
  "test:coverage": "vitest run --coverage",
25
25
  "test:watch": "vitest",