pptx-glimpse 0.10.0 → 0.10.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 +242 -20
  2. package/dist/index.js +242 -20
  3. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -2044,6 +2044,17 @@ function parseDefaultRunProperties(defRPr, colorResolver) {
2044
2044
  }
2045
2045
  return Object.keys(result).length > 0 ? result : void 0;
2046
2046
  }
2047
+ var VALID_AUTO_NUM_SCHEMES = /* @__PURE__ */ new Set([
2048
+ "arabicPeriod",
2049
+ "arabicParenR",
2050
+ "romanUcPeriod",
2051
+ "romanLcPeriod",
2052
+ "alphaUcPeriod",
2053
+ "alphaLcPeriod",
2054
+ "alphaLcParenR",
2055
+ "alphaUcParenR",
2056
+ "arabicPlain"
2057
+ ]);
2047
2058
  function parseParagraphLevelProperties(node, colorResolver) {
2048
2059
  if (!node) return void 0;
2049
2060
  const result = {};
@@ -2056,6 +2067,36 @@ function parseParagraphLevelProperties(node, colorResolver) {
2056
2067
  if (node["@_indent"] !== void 0) {
2057
2068
  result.indent = asEmu(Number(node["@_indent"]));
2058
2069
  }
2070
+ if (node.buNone !== void 0) {
2071
+ result.bullet = { type: "none" };
2072
+ } else if (node.buChar) {
2073
+ const buChar = node.buChar;
2074
+ result.bullet = { type: "char", char: buChar["@_char"] ?? "\u2022" };
2075
+ } else if (node.buAutoNum) {
2076
+ const buAutoNum = node.buAutoNum;
2077
+ const scheme = buAutoNum["@_type"] ?? "arabicPeriod";
2078
+ result.bullet = {
2079
+ type: "autoNum",
2080
+ scheme: VALID_AUTO_NUM_SCHEMES.has(scheme) ? scheme : "arabicPeriod",
2081
+ startAt: Number(buAutoNum["@_startAt"] ?? 1)
2082
+ };
2083
+ }
2084
+ if (node.buFont) {
2085
+ const buFont = node.buFont;
2086
+ const typeface = buFont["@_typeface"] ?? null;
2087
+ if (typeface) result.bulletFont = typeface;
2088
+ }
2089
+ if (colorResolver) {
2090
+ const buClr = node.buClr;
2091
+ if (buClr) {
2092
+ const color = colorResolver.resolve(buClr);
2093
+ if (color) result.bulletColor = color;
2094
+ }
2095
+ }
2096
+ const buSzPct = node.buSzPct;
2097
+ if (buSzPct) {
2098
+ result.bulletSizePct = Number(buSzPct["@_val"]);
2099
+ }
2059
2100
  const defRPr = parseDefaultRunProperties(node.defRPr, colorResolver);
2060
2101
  if (defRPr) {
2061
2102
  result.defaultRunProperties = defRPr;
@@ -3870,6 +3911,114 @@ function findMatchingPlaceholder(placeholderType, placeholderIdx, styles) {
3870
3911
  }
3871
3912
  return void 0;
3872
3913
  }
3914
+ function hasMultipleBulletPPr(p, orderedChildren) {
3915
+ const rawPPr = p.pPr;
3916
+ const pPrList = Array.isArray(rawPPr) ? rawPPr : rawPPr ? [rawPPr] : [];
3917
+ let bulletPPrCount = 0;
3918
+ let pPrCounter = 0;
3919
+ for (const child of orderedChildren) {
3920
+ const tag = Object.keys(child).find((k) => k !== ":@");
3921
+ if (tag === "pPr") {
3922
+ const pPrNode = pPrList[pPrCounter];
3923
+ if (pPrNode && (pPrNode.buChar !== void 0 || pPrNode.buAutoNum !== void 0 || pPrNode.buBlip !== void 0)) {
3924
+ bulletPPrCount++;
3925
+ if (bulletPPrCount >= 2) return true;
3926
+ }
3927
+ pPrCounter++;
3928
+ }
3929
+ }
3930
+ return false;
3931
+ }
3932
+ function splitInterleavedParagraph(p, orderedPChildren, colorResolver, rels, fontScheme, lstStyle) {
3933
+ const rawPPr = p.pPr;
3934
+ const pPrList = Array.isArray(rawPPr) ? rawPPr : rawPPr ? [rawPPr] : [];
3935
+ const rList = p.r ?? [];
3936
+ const fldList = p.fld ?? [];
3937
+ const brList = p.br ?? [];
3938
+ const groups = [];
3939
+ let currentGroup = null;
3940
+ let pPrCounter = 0;
3941
+ const tagCounters = {};
3942
+ for (const child of orderedPChildren) {
3943
+ const tag = Object.keys(child).find((k) => k !== ":@");
3944
+ if (!tag) continue;
3945
+ if (tag === "pPr") {
3946
+ const pPrNode = pPrList[pPrCounter];
3947
+ const hasBullet = pPrNode && (pPrNode.buChar !== void 0 || pPrNode.buAutoNum !== void 0 || pPrNode.buBlip !== void 0);
3948
+ if (hasBullet || !currentGroup) {
3949
+ if (currentGroup) groups.push(currentGroup);
3950
+ currentGroup = {
3951
+ pPrIdx: pPrCounter,
3952
+ rIndices: [],
3953
+ fldIndices: [],
3954
+ brIndices: [],
3955
+ orderedChildren: [child]
3956
+ };
3957
+ } else {
3958
+ currentGroup.orderedChildren.push(child);
3959
+ }
3960
+ pPrCounter++;
3961
+ } else if (tag === "endParaRPr") {
3962
+ } else {
3963
+ const globalIdx = tagCounters[tag] ?? 0;
3964
+ tagCounters[tag] = globalIdx + 1;
3965
+ if (!currentGroup) {
3966
+ currentGroup = {
3967
+ pPrIdx: -1,
3968
+ rIndices: [],
3969
+ fldIndices: [],
3970
+ brIndices: [],
3971
+ orderedChildren: []
3972
+ };
3973
+ }
3974
+ if (tag === "r") currentGroup.rIndices.push(globalIdx);
3975
+ else if (tag === "fld") currentGroup.fldIndices.push(globalIdx);
3976
+ else if (tag === "br") currentGroup.brIndices.push(globalIdx);
3977
+ currentGroup.orderedChildren.push(child);
3978
+ }
3979
+ }
3980
+ if (currentGroup) groups.push(currentGroup);
3981
+ const results = [];
3982
+ for (let g = 0; g < groups.length; g++) {
3983
+ const group = groups[g];
3984
+ const isLast = g === groups.length - 1;
3985
+ const syntheticP = {};
3986
+ if (group.pPrIdx >= 0 && pPrList[group.pPrIdx]) {
3987
+ syntheticP.pPr = pPrList[group.pPrIdx];
3988
+ }
3989
+ if (group.rIndices.length > 0) {
3990
+ syntheticP.r = group.rIndices.map((i) => rList[i]);
3991
+ }
3992
+ if (group.fldIndices.length > 0) {
3993
+ syntheticP.fld = group.fldIndices.map((i) => fldList[i]);
3994
+ }
3995
+ if (group.brIndices.length > 0) {
3996
+ syntheticP.br = group.brIndices.map((i) => brList[i]);
3997
+ }
3998
+ if (isLast && p.endParaRPr) {
3999
+ syntheticP.endParaRPr = p.endParaRPr;
4000
+ }
4001
+ const paragraph = parseParagraph(
4002
+ syntheticP,
4003
+ colorResolver,
4004
+ rels,
4005
+ fontScheme,
4006
+ lstStyle,
4007
+ group.orderedChildren
4008
+ );
4009
+ if (!isLast && paragraph.runs.length > 0) {
4010
+ const lastRun = paragraph.runs[paragraph.runs.length - 1];
4011
+ if (lastRun.text.endsWith("\n")) {
4012
+ paragraph.runs[paragraph.runs.length - 1] = {
4013
+ ...lastRun,
4014
+ text: lastRun.text.slice(0, -1)
4015
+ };
4016
+ }
4017
+ }
4018
+ results.push(paragraph);
4019
+ }
4020
+ return results;
4021
+ }
3873
4022
  function parseTextBody(txBody, colorResolver, rels, fontScheme, lstStyleOverride, orderedTxBody) {
3874
4023
  if (!txBody) return null;
3875
4024
  const bodyPr = txBody.bodyPr;
@@ -3914,14 +4063,28 @@ function parseTextBody(txBody, colorResolver, rels, fontScheme, lstStyleOverride
3914
4063
  const paragraphs = [];
3915
4064
  const pList = txBody.p ?? [];
3916
4065
  for (let i = 0; i < pList.length; i++) {
3917
- paragraphs.push(
3918
- parseParagraph(pList[i], colorResolver, rels, fontScheme, lstStyle, orderedParagraphs[i])
3919
- );
4066
+ const orderedChildren = orderedParagraphs[i];
4067
+ if (orderedChildren && hasMultipleBulletPPr(pList[i], orderedChildren)) {
4068
+ paragraphs.push(
4069
+ ...splitInterleavedParagraph(
4070
+ pList[i],
4071
+ orderedChildren,
4072
+ colorResolver,
4073
+ rels,
4074
+ fontScheme,
4075
+ lstStyle
4076
+ )
4077
+ );
4078
+ } else {
4079
+ paragraphs.push(
4080
+ parseParagraph(pList[i], colorResolver, rels, fontScheme, lstStyle, orderedChildren)
4081
+ );
4082
+ }
3920
4083
  }
3921
4084
  if (paragraphs.length === 0) return null;
3922
4085
  return { paragraphs, bodyProperties };
3923
4086
  }
3924
- var VALID_AUTO_NUM_SCHEMES = /* @__PURE__ */ new Set([
4087
+ var VALID_AUTO_NUM_SCHEMES2 = /* @__PURE__ */ new Set([
3925
4088
  "arabicPeriod",
3926
4089
  "arabicParenR",
3927
4090
  "romanUcPeriod",
@@ -3950,7 +4113,7 @@ function parseBullet(pPr, colorResolver) {
3950
4113
  const scheme = buAutoNum["@_type"] ?? "arabicPeriod";
3951
4114
  bullet = {
3952
4115
  type: "autoNum",
3953
- scheme: VALID_AUTO_NUM_SCHEMES.has(scheme) ? scheme : "arabicPeriod",
4116
+ scheme: VALID_AUTO_NUM_SCHEMES2.has(scheme) ? scheme : "arabicPeriod",
3954
4117
  startAt: Number(buAutoNum["@_startAt"] ?? 1)
3955
4118
  };
3956
4119
  }
@@ -4024,12 +4187,12 @@ function parseParagraph(p, colorResolver, rels, fontScheme, lstStyle, orderedPCh
4024
4187
  spaceBefore: parseSpacing(pPr?.spcBef),
4025
4188
  spaceAfter: parseSpacing(pPr?.spcAft),
4026
4189
  level,
4027
- bullet,
4028
- bulletFont,
4029
- bulletColor,
4030
- bulletSizePct,
4031
- marginLeft: pPr?.["@_marL"] !== void 0 ? asEmu(Number(pPr["@_marL"])) : lstLevelProps?.marginLeft ?? asEmu(0),
4032
- indent: pPr?.["@_indent"] !== void 0 ? asEmu(Number(pPr["@_indent"])) : lstLevelProps?.indent ?? asEmu(0),
4190
+ bullet: bullet ?? lstLevelProps?.bullet ?? null,
4191
+ bulletFont: bulletFont ?? lstLevelProps?.bulletFont ?? null,
4192
+ bulletColor: bulletColor ?? lstLevelProps?.bulletColor ?? null,
4193
+ bulletSizePct: bulletSizePct ?? lstLevelProps?.bulletSizePct ?? null,
4194
+ marginLeft: pPr?.["@_marL"] !== void 0 ? asEmu(Number(pPr["@_marL"])) : lstLevelProps?.marginLeft ?? null,
4195
+ indent: pPr?.["@_indent"] !== void 0 ? asEmu(Number(pPr["@_indent"])) : lstLevelProps?.indent ?? null,
4033
4196
  tabStops
4034
4197
  };
4035
4198
  const pPrDefRPr = parseDefaultRunProperties(pPr?.defRPr);
@@ -4186,7 +4349,8 @@ function parseRunProperties(rPr, colorResolver, rels, fontScheme, defaults) {
4186
4349
  );
4187
4350
  const bold = rPr["@_b"] !== void 0 ? rPr["@_b"] === "1" || rPr["@_b"] === "true" : defaults?.bold ?? false;
4188
4351
  const italic = rPr["@_i"] !== void 0 ? rPr["@_i"] === "1" || rPr["@_i"] === "true" : defaults?.italic ?? false;
4189
- const underline = rPr["@_u"] !== void 0 ? rPr["@_u"] !== "none" : defaults?.underline ?? false;
4352
+ const hasExplicitUnderline = rPr["@_u"] !== void 0;
4353
+ let underline = hasExplicitUnderline ? rPr["@_u"] !== "none" : defaults?.underline ?? false;
4190
4354
  const strikethrough = rPr["@_strike"] !== void 0 ? rPr["@_strike"] !== "noStrike" : defaults?.strikethrough ?? false;
4191
4355
  const baseline = rPr["@_baseline"] ? Number(rPr["@_baseline"]) / 1e3 : 0;
4192
4356
  const solidFill = rPr.solidFill;
@@ -4195,6 +4359,14 @@ function parseRunProperties(rPr, colorResolver, rels, fontScheme, defaults) {
4195
4359
  color = null;
4196
4360
  }
4197
4361
  const hyperlink = parseHyperlink(rPr.hlinkClick, rels);
4362
+ if (hyperlink) {
4363
+ if (!color) {
4364
+ color = colorResolver.resolve({ schemeClr: { "@_val": "hlink" } });
4365
+ }
4366
+ if (!hasExplicitUnderline && !underline) {
4367
+ underline = true;
4368
+ }
4369
+ }
4198
4370
  const ln = rPr.ln;
4199
4371
  let outline = null;
4200
4372
  if (ln) {
@@ -4716,6 +4888,51 @@ function resolveShapeTextInheritance(shape, context) {
4716
4888
  paragraph.properties.alignment = "l";
4717
4889
  }
4718
4890
  }
4891
+ if (paragraph.properties.bullet === null) {
4892
+ for (const source of chainSources) {
4893
+ if (!source) continue;
4894
+ const levelProps = source.levels[level] ?? source.defaultParagraph;
4895
+ if (!levelProps?.bullet) continue;
4896
+ paragraph.properties.bullet = levelProps.bullet;
4897
+ break;
4898
+ }
4899
+ }
4900
+ const hasBullet = paragraph.properties.bullet !== null && paragraph.properties.bullet.type !== "none";
4901
+ if (hasBullet && (paragraph.properties.bulletFont === null || paragraph.properties.bulletColor === null || paragraph.properties.bulletSizePct === null)) {
4902
+ for (const source of chainSources) {
4903
+ if (!source) continue;
4904
+ const levelProps = source.levels[level] ?? source.defaultParagraph;
4905
+ if (!levelProps) continue;
4906
+ if (paragraph.properties.bulletFont === null && levelProps.bulletFont) {
4907
+ paragraph.properties.bulletFont = levelProps.bulletFont;
4908
+ }
4909
+ if (paragraph.properties.bulletColor === null && levelProps.bulletColor) {
4910
+ paragraph.properties.bulletColor = levelProps.bulletColor;
4911
+ }
4912
+ if (paragraph.properties.bulletSizePct === null && levelProps.bulletSizePct) {
4913
+ paragraph.properties.bulletSizePct = levelProps.bulletSizePct;
4914
+ }
4915
+ if (paragraph.properties.bulletFont !== null && paragraph.properties.bulletColor !== null && paragraph.properties.bulletSizePct !== null) {
4916
+ break;
4917
+ }
4918
+ }
4919
+ }
4920
+ if (paragraph.properties.marginLeft === null || paragraph.properties.indent === null) {
4921
+ for (const source of chainSources) {
4922
+ if (!source) continue;
4923
+ const levelProps = source.levels[level] ?? source.defaultParagraph;
4924
+ if (!levelProps) continue;
4925
+ if (paragraph.properties.marginLeft === null && levelProps.marginLeft !== void 0) {
4926
+ paragraph.properties.marginLeft = levelProps.marginLeft;
4927
+ }
4928
+ if (paragraph.properties.indent === null && levelProps.indent !== void 0) {
4929
+ paragraph.properties.indent = levelProps.indent;
4930
+ }
4931
+ if (paragraph.properties.marginLeft !== null && paragraph.properties.indent !== null) {
4932
+ break;
4933
+ }
4934
+ }
4935
+ }
4719
4936
  for (const run of paragraph.runs) {
4720
4937
  const props = run.properties;
4721
4938
  for (const source of chainSources) {
@@ -7691,8 +7908,8 @@ function renderTextBody(textBody, transform) {
7691
7908
  const autoNumCounters = /* @__PURE__ */ new Map();
7692
7909
  let prevSpaceAfterPx = 0;
7693
7910
  for (const para of paragraphs) {
7694
- const paraMarginLeft = emuToPixels(para.properties.marginLeft);
7695
- const paraIndent = emuToPixels(para.properties.indent);
7911
+ const paraMarginLeft = emuToPixels(para.properties.marginLeft ?? asEmu(0));
7912
+ const paraIndent = emuToPixels(para.properties.indent ?? asEmu(0));
7696
7913
  const textStartX = marginLeftPx + paraMarginLeft;
7697
7914
  const bulletX = textStartX + paraIndent;
7698
7915
  const effectiveTextWidth = textWidth - paraMarginLeft;
@@ -8393,13 +8610,13 @@ function renderSegmentAsPath(text, props, x, y, fontScale, defaultFontSize, font
8393
8610
  }
8394
8611
  return { svg, width: totalWidth };
8395
8612
  }
8396
- function renderBulletAsPath(bulletText, x, y, paraProps, textFontSizePt, fontScale, fontResolver) {
8613
+ function renderBulletAsPath(bulletText, x, y, paraProps, textFontSizePt, fontScale, fontResolver, runFontFamily, runFontFamilyEa) {
8397
8614
  let bulletFontSize = textFontSizePt;
8398
8615
  if (paraProps.bulletSizePct !== null) {
8399
8616
  bulletFontSize = textFontSizePt * (paraProps.bulletSizePct / 1e5);
8400
8617
  }
8401
8618
  const fontSizePx = bulletFontSize * PX_PER_PT3;
8402
- const font = fontResolver.resolveFont(paraProps.bulletFont, null);
8619
+ const font = paraProps.bulletFont ? fontResolver.resolveFont(paraProps.bulletFont, null) : fontResolver.resolveFont(runFontFamily ?? null, runFontFamilyEa ?? null);
8403
8620
  if (!font) return [];
8404
8621
  const path = font.getPath(bulletText, x, y, fontSizePx);
8405
8622
  const pathData = path.toPathData(2);
@@ -8467,8 +8684,8 @@ function renderTextBodyAsPath(textBody, transform, fontResolver) {
8467
8684
  const autoNumCounters = /* @__PURE__ */ new Map();
8468
8685
  let prevSpaceAfterPx = 0;
8469
8686
  for (const para of paragraphs) {
8470
- const paraMarginLeft = emuToPixels(para.properties.marginLeft);
8471
- const paraIndent = emuToPixels(para.properties.indent);
8687
+ const paraMarginLeft = emuToPixels(para.properties.marginLeft ?? asEmu(0));
8688
+ const paraIndent = emuToPixels(para.properties.indent ?? asEmu(0));
8472
8689
  const textStartX = marginLeftPx + paraMarginLeft;
8473
8690
  const bulletX = textStartX + paraIndent;
8474
8691
  const effectiveTextWidth = textWidth - paraMarginLeft;
@@ -8522,6 +8739,7 @@ function renderTextBodyAsPath(textBody, transform, fontResolver) {
8522
8739
  let currentX = lineStartX;
8523
8740
  if (lineIdx === 0 && bulletText) {
8524
8741
  const lineFontSize = getLineFontSize(line.segments, defaultFontSize) * fontScale;
8742
+ const firstSeg = line.segments[0];
8525
8743
  elements.push(
8526
8744
  ...renderBulletAsPath(
8527
8745
  bulletText,
@@ -8530,7 +8748,9 @@ function renderTextBodyAsPath(textBody, transform, fontResolver) {
8530
8748
  para.properties,
8531
8749
  lineFontSize,
8532
8750
  fontScale,
8533
- fontResolver
8751
+ fontResolver,
8752
+ firstSeg?.properties.fontFamily,
8753
+ firstSeg?.properties.fontFamilyEa
8534
8754
  )
8535
8755
  );
8536
8756
  }
@@ -8577,7 +8797,9 @@ function renderTextBodyAsPath(textBody, transform, fontResolver) {
8577
8797
  para.properties,
8578
8798
  fontSize,
8579
8799
  fontScale,
8580
- fontResolver
8800
+ fontResolver,
8801
+ firstRun?.properties.fontFamily,
8802
+ firstRun?.properties.fontFamilyEa
8581
8803
  )
8582
8804
  );
8583
8805
  }
package/dist/index.js CHANGED
@@ -2006,6 +2006,17 @@ function parseDefaultRunProperties(defRPr, colorResolver) {
2006
2006
  }
2007
2007
  return Object.keys(result).length > 0 ? result : void 0;
2008
2008
  }
2009
+ var VALID_AUTO_NUM_SCHEMES = /* @__PURE__ */ new Set([
2010
+ "arabicPeriod",
2011
+ "arabicParenR",
2012
+ "romanUcPeriod",
2013
+ "romanLcPeriod",
2014
+ "alphaUcPeriod",
2015
+ "alphaLcPeriod",
2016
+ "alphaLcParenR",
2017
+ "alphaUcParenR",
2018
+ "arabicPlain"
2019
+ ]);
2009
2020
  function parseParagraphLevelProperties(node, colorResolver) {
2010
2021
  if (!node) return void 0;
2011
2022
  const result = {};
@@ -2018,6 +2029,36 @@ function parseParagraphLevelProperties(node, colorResolver) {
2018
2029
  if (node["@_indent"] !== void 0) {
2019
2030
  result.indent = asEmu(Number(node["@_indent"]));
2020
2031
  }
2032
+ if (node.buNone !== void 0) {
2033
+ result.bullet = { type: "none" };
2034
+ } else if (node.buChar) {
2035
+ const buChar = node.buChar;
2036
+ result.bullet = { type: "char", char: buChar["@_char"] ?? "\u2022" };
2037
+ } else if (node.buAutoNum) {
2038
+ const buAutoNum = node.buAutoNum;
2039
+ const scheme = buAutoNum["@_type"] ?? "arabicPeriod";
2040
+ result.bullet = {
2041
+ type: "autoNum",
2042
+ scheme: VALID_AUTO_NUM_SCHEMES.has(scheme) ? scheme : "arabicPeriod",
2043
+ startAt: Number(buAutoNum["@_startAt"] ?? 1)
2044
+ };
2045
+ }
2046
+ if (node.buFont) {
2047
+ const buFont = node.buFont;
2048
+ const typeface = buFont["@_typeface"] ?? null;
2049
+ if (typeface) result.bulletFont = typeface;
2050
+ }
2051
+ if (colorResolver) {
2052
+ const buClr = node.buClr;
2053
+ if (buClr) {
2054
+ const color = colorResolver.resolve(buClr);
2055
+ if (color) result.bulletColor = color;
2056
+ }
2057
+ }
2058
+ const buSzPct = node.buSzPct;
2059
+ if (buSzPct) {
2060
+ result.bulletSizePct = Number(buSzPct["@_val"]);
2061
+ }
2021
2062
  const defRPr = parseDefaultRunProperties(node.defRPr, colorResolver);
2022
2063
  if (defRPr) {
2023
2064
  result.defaultRunProperties = defRPr;
@@ -3832,6 +3873,114 @@ function findMatchingPlaceholder(placeholderType, placeholderIdx, styles) {
3832
3873
  }
3833
3874
  return void 0;
3834
3875
  }
3876
+ function hasMultipleBulletPPr(p, orderedChildren) {
3877
+ const rawPPr = p.pPr;
3878
+ const pPrList = Array.isArray(rawPPr) ? rawPPr : rawPPr ? [rawPPr] : [];
3879
+ let bulletPPrCount = 0;
3880
+ let pPrCounter = 0;
3881
+ for (const child of orderedChildren) {
3882
+ const tag = Object.keys(child).find((k) => k !== ":@");
3883
+ if (tag === "pPr") {
3884
+ const pPrNode = pPrList[pPrCounter];
3885
+ if (pPrNode && (pPrNode.buChar !== void 0 || pPrNode.buAutoNum !== void 0 || pPrNode.buBlip !== void 0)) {
3886
+ bulletPPrCount++;
3887
+ if (bulletPPrCount >= 2) return true;
3888
+ }
3889
+ pPrCounter++;
3890
+ }
3891
+ }
3892
+ return false;
3893
+ }
3894
+ function splitInterleavedParagraph(p, orderedPChildren, colorResolver, rels, fontScheme, lstStyle) {
3895
+ const rawPPr = p.pPr;
3896
+ const pPrList = Array.isArray(rawPPr) ? rawPPr : rawPPr ? [rawPPr] : [];
3897
+ const rList = p.r ?? [];
3898
+ const fldList = p.fld ?? [];
3899
+ const brList = p.br ?? [];
3900
+ const groups = [];
3901
+ let currentGroup = null;
3902
+ let pPrCounter = 0;
3903
+ const tagCounters = {};
3904
+ for (const child of orderedPChildren) {
3905
+ const tag = Object.keys(child).find((k) => k !== ":@");
3906
+ if (!tag) continue;
3907
+ if (tag === "pPr") {
3908
+ const pPrNode = pPrList[pPrCounter];
3909
+ const hasBullet = pPrNode && (pPrNode.buChar !== void 0 || pPrNode.buAutoNum !== void 0 || pPrNode.buBlip !== void 0);
3910
+ if (hasBullet || !currentGroup) {
3911
+ if (currentGroup) groups.push(currentGroup);
3912
+ currentGroup = {
3913
+ pPrIdx: pPrCounter,
3914
+ rIndices: [],
3915
+ fldIndices: [],
3916
+ brIndices: [],
3917
+ orderedChildren: [child]
3918
+ };
3919
+ } else {
3920
+ currentGroup.orderedChildren.push(child);
3921
+ }
3922
+ pPrCounter++;
3923
+ } else if (tag === "endParaRPr") {
3924
+ } else {
3925
+ const globalIdx = tagCounters[tag] ?? 0;
3926
+ tagCounters[tag] = globalIdx + 1;
3927
+ if (!currentGroup) {
3928
+ currentGroup = {
3929
+ pPrIdx: -1,
3930
+ rIndices: [],
3931
+ fldIndices: [],
3932
+ brIndices: [],
3933
+ orderedChildren: []
3934
+ };
3935
+ }
3936
+ if (tag === "r") currentGroup.rIndices.push(globalIdx);
3937
+ else if (tag === "fld") currentGroup.fldIndices.push(globalIdx);
3938
+ else if (tag === "br") currentGroup.brIndices.push(globalIdx);
3939
+ currentGroup.orderedChildren.push(child);
3940
+ }
3941
+ }
3942
+ if (currentGroup) groups.push(currentGroup);
3943
+ const results = [];
3944
+ for (let g = 0; g < groups.length; g++) {
3945
+ const group = groups[g];
3946
+ const isLast = g === groups.length - 1;
3947
+ const syntheticP = {};
3948
+ if (group.pPrIdx >= 0 && pPrList[group.pPrIdx]) {
3949
+ syntheticP.pPr = pPrList[group.pPrIdx];
3950
+ }
3951
+ if (group.rIndices.length > 0) {
3952
+ syntheticP.r = group.rIndices.map((i) => rList[i]);
3953
+ }
3954
+ if (group.fldIndices.length > 0) {
3955
+ syntheticP.fld = group.fldIndices.map((i) => fldList[i]);
3956
+ }
3957
+ if (group.brIndices.length > 0) {
3958
+ syntheticP.br = group.brIndices.map((i) => brList[i]);
3959
+ }
3960
+ if (isLast && p.endParaRPr) {
3961
+ syntheticP.endParaRPr = p.endParaRPr;
3962
+ }
3963
+ const paragraph = parseParagraph(
3964
+ syntheticP,
3965
+ colorResolver,
3966
+ rels,
3967
+ fontScheme,
3968
+ lstStyle,
3969
+ group.orderedChildren
3970
+ );
3971
+ if (!isLast && paragraph.runs.length > 0) {
3972
+ const lastRun = paragraph.runs[paragraph.runs.length - 1];
3973
+ if (lastRun.text.endsWith("\n")) {
3974
+ paragraph.runs[paragraph.runs.length - 1] = {
3975
+ ...lastRun,
3976
+ text: lastRun.text.slice(0, -1)
3977
+ };
3978
+ }
3979
+ }
3980
+ results.push(paragraph);
3981
+ }
3982
+ return results;
3983
+ }
3835
3984
  function parseTextBody(txBody, colorResolver, rels, fontScheme, lstStyleOverride, orderedTxBody) {
3836
3985
  if (!txBody) return null;
3837
3986
  const bodyPr = txBody.bodyPr;
@@ -3876,14 +4025,28 @@ function parseTextBody(txBody, colorResolver, rels, fontScheme, lstStyleOverride
3876
4025
  const paragraphs = [];
3877
4026
  const pList = txBody.p ?? [];
3878
4027
  for (let i = 0; i < pList.length; i++) {
3879
- paragraphs.push(
3880
- parseParagraph(pList[i], colorResolver, rels, fontScheme, lstStyle, orderedParagraphs[i])
3881
- );
4028
+ const orderedChildren = orderedParagraphs[i];
4029
+ if (orderedChildren && hasMultipleBulletPPr(pList[i], orderedChildren)) {
4030
+ paragraphs.push(
4031
+ ...splitInterleavedParagraph(
4032
+ pList[i],
4033
+ orderedChildren,
4034
+ colorResolver,
4035
+ rels,
4036
+ fontScheme,
4037
+ lstStyle
4038
+ )
4039
+ );
4040
+ } else {
4041
+ paragraphs.push(
4042
+ parseParagraph(pList[i], colorResolver, rels, fontScheme, lstStyle, orderedChildren)
4043
+ );
4044
+ }
3882
4045
  }
3883
4046
  if (paragraphs.length === 0) return null;
3884
4047
  return { paragraphs, bodyProperties };
3885
4048
  }
3886
- var VALID_AUTO_NUM_SCHEMES = /* @__PURE__ */ new Set([
4049
+ var VALID_AUTO_NUM_SCHEMES2 = /* @__PURE__ */ new Set([
3887
4050
  "arabicPeriod",
3888
4051
  "arabicParenR",
3889
4052
  "romanUcPeriod",
@@ -3912,7 +4075,7 @@ function parseBullet(pPr, colorResolver) {
3912
4075
  const scheme = buAutoNum["@_type"] ?? "arabicPeriod";
3913
4076
  bullet = {
3914
4077
  type: "autoNum",
3915
- scheme: VALID_AUTO_NUM_SCHEMES.has(scheme) ? scheme : "arabicPeriod",
4078
+ scheme: VALID_AUTO_NUM_SCHEMES2.has(scheme) ? scheme : "arabicPeriod",
3916
4079
  startAt: Number(buAutoNum["@_startAt"] ?? 1)
3917
4080
  };
3918
4081
  }
@@ -3986,12 +4149,12 @@ function parseParagraph(p, colorResolver, rels, fontScheme, lstStyle, orderedPCh
3986
4149
  spaceBefore: parseSpacing(pPr?.spcBef),
3987
4150
  spaceAfter: parseSpacing(pPr?.spcAft),
3988
4151
  level,
3989
- bullet,
3990
- bulletFont,
3991
- bulletColor,
3992
- bulletSizePct,
3993
- marginLeft: pPr?.["@_marL"] !== void 0 ? asEmu(Number(pPr["@_marL"])) : lstLevelProps?.marginLeft ?? asEmu(0),
3994
- indent: pPr?.["@_indent"] !== void 0 ? asEmu(Number(pPr["@_indent"])) : lstLevelProps?.indent ?? asEmu(0),
4152
+ bullet: bullet ?? lstLevelProps?.bullet ?? null,
4153
+ bulletFont: bulletFont ?? lstLevelProps?.bulletFont ?? null,
4154
+ bulletColor: bulletColor ?? lstLevelProps?.bulletColor ?? null,
4155
+ bulletSizePct: bulletSizePct ?? lstLevelProps?.bulletSizePct ?? null,
4156
+ marginLeft: pPr?.["@_marL"] !== void 0 ? asEmu(Number(pPr["@_marL"])) : lstLevelProps?.marginLeft ?? null,
4157
+ indent: pPr?.["@_indent"] !== void 0 ? asEmu(Number(pPr["@_indent"])) : lstLevelProps?.indent ?? null,
3995
4158
  tabStops
3996
4159
  };
3997
4160
  const pPrDefRPr = parseDefaultRunProperties(pPr?.defRPr);
@@ -4148,7 +4311,8 @@ function parseRunProperties(rPr, colorResolver, rels, fontScheme, defaults) {
4148
4311
  );
4149
4312
  const bold = rPr["@_b"] !== void 0 ? rPr["@_b"] === "1" || rPr["@_b"] === "true" : defaults?.bold ?? false;
4150
4313
  const italic = rPr["@_i"] !== void 0 ? rPr["@_i"] === "1" || rPr["@_i"] === "true" : defaults?.italic ?? false;
4151
- const underline = rPr["@_u"] !== void 0 ? rPr["@_u"] !== "none" : defaults?.underline ?? false;
4314
+ const hasExplicitUnderline = rPr["@_u"] !== void 0;
4315
+ let underline = hasExplicitUnderline ? rPr["@_u"] !== "none" : defaults?.underline ?? false;
4152
4316
  const strikethrough = rPr["@_strike"] !== void 0 ? rPr["@_strike"] !== "noStrike" : defaults?.strikethrough ?? false;
4153
4317
  const baseline = rPr["@_baseline"] ? Number(rPr["@_baseline"]) / 1e3 : 0;
4154
4318
  const solidFill = rPr.solidFill;
@@ -4157,6 +4321,14 @@ function parseRunProperties(rPr, colorResolver, rels, fontScheme, defaults) {
4157
4321
  color = null;
4158
4322
  }
4159
4323
  const hyperlink = parseHyperlink(rPr.hlinkClick, rels);
4324
+ if (hyperlink) {
4325
+ if (!color) {
4326
+ color = colorResolver.resolve({ schemeClr: { "@_val": "hlink" } });
4327
+ }
4328
+ if (!hasExplicitUnderline && !underline) {
4329
+ underline = true;
4330
+ }
4331
+ }
4160
4332
  const ln = rPr.ln;
4161
4333
  let outline = null;
4162
4334
  if (ln) {
@@ -4678,6 +4850,51 @@ function resolveShapeTextInheritance(shape, context) {
4678
4850
  paragraph.properties.alignment = "l";
4679
4851
  }
4680
4852
  }
4853
+ if (paragraph.properties.bullet === null) {
4854
+ for (const source of chainSources) {
4855
+ if (!source) continue;
4856
+ const levelProps = source.levels[level] ?? source.defaultParagraph;
4857
+ if (!levelProps?.bullet) continue;
4858
+ paragraph.properties.bullet = levelProps.bullet;
4859
+ break;
4860
+ }
4861
+ }
4862
+ const hasBullet = paragraph.properties.bullet !== null && paragraph.properties.bullet.type !== "none";
4863
+ if (hasBullet && (paragraph.properties.bulletFont === null || paragraph.properties.bulletColor === null || paragraph.properties.bulletSizePct === null)) {
4864
+ for (const source of chainSources) {
4865
+ if (!source) continue;
4866
+ const levelProps = source.levels[level] ?? source.defaultParagraph;
4867
+ if (!levelProps) continue;
4868
+ if (paragraph.properties.bulletFont === null && levelProps.bulletFont) {
4869
+ paragraph.properties.bulletFont = levelProps.bulletFont;
4870
+ }
4871
+ if (paragraph.properties.bulletColor === null && levelProps.bulletColor) {
4872
+ paragraph.properties.bulletColor = levelProps.bulletColor;
4873
+ }
4874
+ if (paragraph.properties.bulletSizePct === null && levelProps.bulletSizePct) {
4875
+ paragraph.properties.bulletSizePct = levelProps.bulletSizePct;
4876
+ }
4877
+ if (paragraph.properties.bulletFont !== null && paragraph.properties.bulletColor !== null && paragraph.properties.bulletSizePct !== null) {
4878
+ break;
4879
+ }
4880
+ }
4881
+ }
4882
+ if (paragraph.properties.marginLeft === null || paragraph.properties.indent === null) {
4883
+ for (const source of chainSources) {
4884
+ if (!source) continue;
4885
+ const levelProps = source.levels[level] ?? source.defaultParagraph;
4886
+ if (!levelProps) continue;
4887
+ if (paragraph.properties.marginLeft === null && levelProps.marginLeft !== void 0) {
4888
+ paragraph.properties.marginLeft = levelProps.marginLeft;
4889
+ }
4890
+ if (paragraph.properties.indent === null && levelProps.indent !== void 0) {
4891
+ paragraph.properties.indent = levelProps.indent;
4892
+ }
4893
+ if (paragraph.properties.marginLeft !== null && paragraph.properties.indent !== null) {
4894
+ break;
4895
+ }
4896
+ }
4897
+ }
4681
4898
  for (const run of paragraph.runs) {
4682
4899
  const props = run.properties;
4683
4900
  for (const source of chainSources) {
@@ -7653,8 +7870,8 @@ function renderTextBody(textBody, transform) {
7653
7870
  const autoNumCounters = /* @__PURE__ */ new Map();
7654
7871
  let prevSpaceAfterPx = 0;
7655
7872
  for (const para of paragraphs) {
7656
- const paraMarginLeft = emuToPixels(para.properties.marginLeft);
7657
- const paraIndent = emuToPixels(para.properties.indent);
7873
+ const paraMarginLeft = emuToPixels(para.properties.marginLeft ?? asEmu(0));
7874
+ const paraIndent = emuToPixels(para.properties.indent ?? asEmu(0));
7658
7875
  const textStartX = marginLeftPx + paraMarginLeft;
7659
7876
  const bulletX = textStartX + paraIndent;
7660
7877
  const effectiveTextWidth = textWidth - paraMarginLeft;
@@ -8355,13 +8572,13 @@ function renderSegmentAsPath(text, props, x, y, fontScale, defaultFontSize, font
8355
8572
  }
8356
8573
  return { svg, width: totalWidth };
8357
8574
  }
8358
- function renderBulletAsPath(bulletText, x, y, paraProps, textFontSizePt, fontScale, fontResolver) {
8575
+ function renderBulletAsPath(bulletText, x, y, paraProps, textFontSizePt, fontScale, fontResolver, runFontFamily, runFontFamilyEa) {
8359
8576
  let bulletFontSize = textFontSizePt;
8360
8577
  if (paraProps.bulletSizePct !== null) {
8361
8578
  bulletFontSize = textFontSizePt * (paraProps.bulletSizePct / 1e5);
8362
8579
  }
8363
8580
  const fontSizePx = bulletFontSize * PX_PER_PT3;
8364
- const font = fontResolver.resolveFont(paraProps.bulletFont, null);
8581
+ const font = paraProps.bulletFont ? fontResolver.resolveFont(paraProps.bulletFont, null) : fontResolver.resolveFont(runFontFamily ?? null, runFontFamilyEa ?? null);
8365
8582
  if (!font) return [];
8366
8583
  const path = font.getPath(bulletText, x, y, fontSizePx);
8367
8584
  const pathData = path.toPathData(2);
@@ -8429,8 +8646,8 @@ function renderTextBodyAsPath(textBody, transform, fontResolver) {
8429
8646
  const autoNumCounters = /* @__PURE__ */ new Map();
8430
8647
  let prevSpaceAfterPx = 0;
8431
8648
  for (const para of paragraphs) {
8432
- const paraMarginLeft = emuToPixels(para.properties.marginLeft);
8433
- const paraIndent = emuToPixels(para.properties.indent);
8649
+ const paraMarginLeft = emuToPixels(para.properties.marginLeft ?? asEmu(0));
8650
+ const paraIndent = emuToPixels(para.properties.indent ?? asEmu(0));
8434
8651
  const textStartX = marginLeftPx + paraMarginLeft;
8435
8652
  const bulletX = textStartX + paraIndent;
8436
8653
  const effectiveTextWidth = textWidth - paraMarginLeft;
@@ -8484,6 +8701,7 @@ function renderTextBodyAsPath(textBody, transform, fontResolver) {
8484
8701
  let currentX = lineStartX;
8485
8702
  if (lineIdx === 0 && bulletText) {
8486
8703
  const lineFontSize = getLineFontSize(line.segments, defaultFontSize) * fontScale;
8704
+ const firstSeg = line.segments[0];
8487
8705
  elements.push(
8488
8706
  ...renderBulletAsPath(
8489
8707
  bulletText,
@@ -8492,7 +8710,9 @@ function renderTextBodyAsPath(textBody, transform, fontResolver) {
8492
8710
  para.properties,
8493
8711
  lineFontSize,
8494
8712
  fontScale,
8495
- fontResolver
8713
+ fontResolver,
8714
+ firstSeg?.properties.fontFamily,
8715
+ firstSeg?.properties.fontFamilyEa
8496
8716
  )
8497
8717
  );
8498
8718
  }
@@ -8539,7 +8759,9 @@ function renderTextBodyAsPath(textBody, transform, fontResolver) {
8539
8759
  para.properties,
8540
8760
  fontSize,
8541
8761
  fontScale,
8542
- fontResolver
8762
+ fontResolver,
8763
+ firstRun?.properties.fontFamily,
8764
+ firstRun?.properties.fontFamilyEa
8543
8765
  )
8544
8766
  );
8545
8767
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pptx-glimpse",
3
- "version": "0.10.0",
3
+ "version": "0.10.2",
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",