@lofcz/pptxtojson 2.2.3 → 2.2.5

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
@@ -3194,17 +3194,18 @@ function genTextBody(textBodyNode, spNode, slideLayoutSpNode, slideMasterSpNode,
3194
3194
  const align = getHorizontalAlign(pNode, spNode, type, slideLayoutSpNode, slideMasterSpNode, warpObj);
3195
3195
  const spacing = getParagraphSpacing(pNode, textBodyNode, slideLayoutSpNode, slideMasterSpNode, type, slideMasterTextStyles, warpObj);
3196
3196
  const indent = getParagraphIndent(pNode, textBodyNode, slideLayoutSpNode, slideMasterSpNode, type, slideMasterTextStyles, warpObj);
3197
- const listType = getListType(pNode);
3197
+ const listType = getListType(pNode, textBodyNode, slideLayoutSpNode, slideMasterSpNode, type, slideMasterTextStyles, warpObj);
3198
3198
  const listLevel = getListLevel(pNode);
3199
3199
  let alignStyle = align;
3200
3200
  if ('distribute' === align) alignStyle = 'justify';
3201
- let styleText = `text-align: ${alignStyle};line-height: 1.2;`;
3201
+ let styleText = `text-align: ${alignStyle};`;
3202
3202
  if ('distribute' === align) styleText += "text-align-last: justify;text-justify: distribute;";
3203
3203
  if (spacing) {
3204
3204
  if (spacing.lineSpacing) styleText += `line-height: ${spacing.lineSpacing};`;
3205
+ else styleText += "line-height: 1.2;";
3205
3206
  if (spacing.spaceBefore) styleText += `margin-top: ${spacing.spaceBefore};`;
3206
3207
  if (spacing.spaceAfter) styleText += `margin-bottom: ${spacing.spaceAfter};`;
3207
- }
3208
+ } else styleText += "line-height: 1.2;";
3208
3209
  if (indent) {
3209
3210
  if (!listType && indent.marginLeft) styleText += `margin-left: ${indent.marginLeft};`;
3210
3211
  if (!listType && indent.textIndent) styleText += `text-indent: ${indent.textIndent};`;
@@ -3299,19 +3300,21 @@ function getMathRunStyleText(mathRun, siblingRun, pNode, textBodyNode, pFontStyl
3299
3300
  if (fontColor && 'string' == typeof fontColor) styleText += `color: ${fontColor};`;
3300
3301
  return styleText;
3301
3302
  }
3302
- function getListType(node) {
3303
- const pPrNode = node['a:pPr'];
3304
- if (!pPrNode) return '';
3305
- if (pPrNode['a:buNone']) return '';
3303
+ function getListType(node, textBodyNode, slideLayoutSpNode, slideMasterSpNode, type, slideMasterTextStyles, warpObj) {
3306
3304
  const hasContent = node['a:r'] || node['a:br'] || node['a:fld'];
3307
3305
  if (!hasContent) return '';
3308
- if (pPrNode['a:buChar']) return 'ul';
3309
- if (pPrNode['a:buAutoNum']) return 'ol';
3306
+ const styleNodes = getParagraphStyleNodes(node, textBodyNode, slideLayoutSpNode, slideMasterSpNode, type, slideMasterTextStyles, warpObj);
3307
+ if (!styleNodes) return '';
3308
+ for (const styleNode of styleNodes){
3309
+ if (styleNode['a:buNone']) break;
3310
+ if (styleNode['a:buChar'] || styleNode['a:buBlip']) return 'ul';
3311
+ if (styleNode['a:buAutoNum']) return 'ol';
3312
+ }
3310
3313
  return '';
3311
3314
  }
3312
3315
  function getListLevel(node) {
3313
3316
  const pPrNode = node['a:pPr'];
3314
- if (!pPrNode) return -1;
3317
+ if (!pPrNode) return 0;
3315
3318
  const lvlNode = getTextByPathList(pPrNode, [
3316
3319
  'attrs',
3317
3320
  'lvl'
@@ -3319,6 +3322,30 @@ function getListLevel(node) {
3319
3322
  if (void 0 !== lvlNode) return parseInt(lvlNode);
3320
3323
  return 0;
3321
3324
  }
3325
+ function schemeColorToCss(color) {
3326
+ if (!color) return '';
3327
+ return color.startsWith('#') ? color : `#${color}`;
3328
+ }
3329
+ function applyHyperlinkRunStyle(styleText, node, warpObj) {
3330
+ let next = styleText;
3331
+ const runStyleNode = getTextByPathList(node, [
3332
+ 'a:rPr'
3333
+ ]);
3334
+ const runFill = runStyleNode ? getFillType(runStyleNode) : '';
3335
+ const runHasOwnColor = 'SOLID_FILL' === runFill || 'GRADIENT_FILL' === runFill;
3336
+ if (!runHasOwnColor) {
3337
+ const hlinkColor = schemeColorToCss(getSchemeColorFromTheme('a:hlink', warpObj)) || '#0563C1';
3338
+ if (/color\s*:/.test(next)) next = next.replace(/color\s*:\s*[^;]+;/, `color: ${hlinkColor};`);
3339
+ else next += `color: ${hlinkColor};`;
3340
+ }
3341
+ const runUnderline = getTextByPathList(node, [
3342
+ 'a:rPr',
3343
+ 'attrs',
3344
+ 'u'
3345
+ ]);
3346
+ if ('none' !== runUnderline && !/text-decoration\s*:\s*underline/.test(next)) next += 'text-decoration: underline;';
3347
+ return next;
3348
+ }
3322
3349
  function genSpanElement(node, pNode, textBodyNode, pFontStyle, slideLayoutSpNode, slideMasterSpNode, type, slideMasterTextStyles, defaultTextStyle, warpObj) {
3323
3350
  const { styleText, text, hasLink, linkURL } = getSpanStyleInfo(node, pNode, textBodyNode, pFontStyle, slideLayoutSpNode, slideMasterSpNode, type, slideMasterTextStyles, defaultTextStyle, warpObj);
3324
3351
  const processedText = text.replace(/\t/g, '    ').replace(/\s/g, ' ');
@@ -3375,6 +3402,7 @@ function getSpanStyleInfo(node, pNode, textBodyNode, pFontStyle, slideLayoutSpNo
3375
3402
  'r:id'
3376
3403
  ]);
3377
3404
  const hasLink = linkID && warpObj['slideResObj'][linkID];
3405
+ if (hasLink) styleText = applyHyperlinkRunStyle(styleText, node, warpObj);
3378
3406
  return {
3379
3407
  styleText,
3380
3408
  text,
@@ -9818,27 +9846,143 @@ async function parse(file, options = {}) {
9818
9846
  }
9819
9847
  };
9820
9848
  }
9849
+ function asNodeArray(node) {
9850
+ if (!node) return [];
9851
+ return node.constructor === Array ? node : [
9852
+ node
9853
+ ];
9854
+ }
9855
+ function sortSlideXml(p1, p2) {
9856
+ const n1 = +(/(\d+)\.xml/.exec(p1)?.[1] || 0);
9857
+ const n2 = +(/(\d+)\.xml/.exec(p2)?.[1] || 0);
9858
+ return n1 - n2;
9859
+ }
9860
+ function normalizePartName(name) {
9861
+ return String(name || '').replace(/\\/g, '/').replace(/^\//, '');
9862
+ }
9863
+ function relationshipsPartFor(partName) {
9864
+ const name = normalizePartName(partName);
9865
+ const slash = name.lastIndexOf('/');
9866
+ const dir = -1 === slash ? '' : name.slice(0, slash + 1);
9867
+ const file = -1 === slash ? name : name.slice(slash + 1);
9868
+ return `${dir}_rels/${file}.rels`;
9869
+ }
9870
+ function resolvePartTarget(sourcePart, target) {
9871
+ const raw = String(target || '').replace(/\\/g, '/');
9872
+ if (!raw) return '';
9873
+ if (raw.startsWith('/')) return normalizePartName(raw);
9874
+ const source = normalizePartName(sourcePart);
9875
+ const slash = source.lastIndexOf('/');
9876
+ const dir = -1 === slash ? '' : source.slice(0, slash + 1);
9877
+ const out = [];
9878
+ for (const seg of `${dir}${raw}`.split('/'))if (seg && '.' !== seg) if ('..' === seg) out.pop();
9879
+ else out.push(seg);
9880
+ return out.join('/');
9881
+ }
9882
+ function relationshipRid(attrs) {
9883
+ if (!attrs) return '';
9884
+ return attrs['r:id'] || attrs.rId || '';
9885
+ }
9886
+ async function getPresentationPart(zip) {
9887
+ const rels = asNodeArray(getTextByPathList(await readXmlFile(zip, '_rels/.rels'), [
9888
+ 'Relationships',
9889
+ 'Relationship'
9890
+ ]));
9891
+ for (const rel of rels){
9892
+ const type = getTextByPathList(rel, [
9893
+ 'attrs',
9894
+ 'Type'
9895
+ ]);
9896
+ const target = getTextByPathList(rel, [
9897
+ 'attrs',
9898
+ 'Target'
9899
+ ]);
9900
+ const targetMode = getTextByPathList(rel, [
9901
+ 'attrs',
9902
+ 'TargetMode'
9903
+ ]);
9904
+ if (!targetMode || 'external' !== String(targetMode).toLowerCase()) {
9905
+ if ('officeDocument' === getRelTypeName(type) && target) {
9906
+ const loc = resolvePartTarget('', target);
9907
+ if (loc) return loc;
9908
+ }
9909
+ }
9910
+ }
9911
+ return 'ppt/presentation.xml';
9912
+ }
9913
+ async function getSlidesFromPresentation(zip) {
9914
+ const presentationPart = await getPresentationPart(zip);
9915
+ const relItems = asNodeArray(getTextByPathList(await readXmlFile(zip, relationshipsPartFor(presentationPart)), [
9916
+ 'Relationships',
9917
+ 'Relationship'
9918
+ ]));
9919
+ const relsMap = {};
9920
+ const slideTargets = [];
9921
+ for (const rel of relItems){
9922
+ const id = getTextByPathList(rel, [
9923
+ 'attrs',
9924
+ 'Id'
9925
+ ]);
9926
+ const type = getTextByPathList(rel, [
9927
+ 'attrs',
9928
+ 'Type'
9929
+ ]);
9930
+ const target = getTextByPathList(rel, [
9931
+ 'attrs',
9932
+ 'Target'
9933
+ ]);
9934
+ const targetMode = getTextByPathList(rel, [
9935
+ 'attrs',
9936
+ 'TargetMode'
9937
+ ]);
9938
+ if (!id || !target) continue;
9939
+ if (targetMode && 'external' === String(targetMode).toLowerCase()) continue;
9940
+ const loc = resolvePartTarget(presentationPart, target);
9941
+ if (loc) {
9942
+ relsMap[id] = loc;
9943
+ if ('slide' === getRelTypeName(type)) slideTargets.push(loc);
9944
+ }
9945
+ }
9946
+ const presentation = await readXmlFile(zip, presentationPart);
9947
+ const sldIds = asNodeArray(getTextByPathList(presentation, [
9948
+ 'p:presentation',
9949
+ 'p:sldIdLst',
9950
+ 'p:sldId'
9951
+ ]));
9952
+ const ordered = [];
9953
+ for (const sldId of sldIds){
9954
+ const rid = relationshipRid(sldId?.attrs);
9955
+ if (rid && relsMap[rid]) ordered.push(relsMap[rid]);
9956
+ }
9957
+ if (ordered.length) return ordered;
9958
+ return slideTargets;
9959
+ }
9821
9960
  async function getContentTypes(zip) {
9822
9961
  const ContentTypesJson = await readXmlFile(zip, '[Content_Types].xml');
9823
- const subObj = ContentTypesJson['Types']['Override'];
9824
- let slidesLocArray = [];
9962
+ const overrides = asNodeArray(getTextByPathList(ContentTypesJson, [
9963
+ 'Types',
9964
+ 'Override'
9965
+ ]));
9966
+ const overrideSlides = [];
9825
9967
  let slideLayoutsLocArray = [];
9826
- for (const item of subObj)switch(item['attrs']['ContentType']){
9827
- case 'application/vnd.openxmlformats-officedocument.presentationml.slide+xml':
9828
- slidesLocArray.push(item['attrs']['PartName'].substr(1));
9829
- break;
9830
- case 'application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml':
9831
- slideLayoutsLocArray.push(item['attrs']['PartName'].substr(1));
9832
- break;
9833
- default:
9968
+ for (const item of overrides){
9969
+ const contentType = item?.attrs?.ContentType;
9970
+ const partName = item?.attrs?.PartName;
9971
+ if (!contentType || !partName) continue;
9972
+ const loc = String(partName).replace(/^\//, '');
9973
+ switch(contentType){
9974
+ case 'application/vnd.openxmlformats-officedocument.presentationml.slide+xml':
9975
+ overrideSlides.push(loc);
9976
+ break;
9977
+ case 'application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml':
9978
+ slideLayoutsLocArray.push(loc);
9979
+ break;
9980
+ default:
9981
+ }
9834
9982
  }
9835
- const sortSlideXml = (p1, p2)=>{
9836
- const n1 = +/(\d+)\.xml/.exec(p1)[1];
9837
- const n2 = +/(\d+)\.xml/.exec(p2)[1];
9838
- return n1 - n2;
9839
- };
9840
- slidesLocArray = slidesLocArray.sort(sortSlideXml);
9841
9983
  slideLayoutsLocArray = slideLayoutsLocArray.sort(sortSlideXml);
9984
+ let slidesLocArray = await getSlidesFromPresentation(zip);
9985
+ if (!slidesLocArray.length) slidesLocArray = overrideSlides.sort(sortSlideXml);
9842
9986
  return {
9843
9987
  slides: slidesLocArray,
9844
9988
  slideLayouts: slideLayoutsLocArray