@lofcz/pptxtojson 2.2.2 → 2.2.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/README.md +13 -0
- package/dist/index.cjs +246 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +29 -0
- package/dist/index.js +246 -15
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -190,6 +190,19 @@ All numeric length values in the output JSON use `pt` (point) as the unit.
|
|
|
190
190
|
- Duration `duration`
|
|
191
191
|
- Direction `direction`
|
|
192
192
|
|
|
193
|
+
- Element animations `animations` (from `p:timing`, presenter order)
|
|
194
|
+
- Target shape id `spid` (matches the element's `id` / `p:cNvPr@id`)
|
|
195
|
+
- Trigger `trigger`: `onClick` | `withPrevious` | `afterPrevious`
|
|
196
|
+
- Preset class `class`: `entr` | `exit` | `emph` | `path` | `verb` | `mediacall`
|
|
197
|
+
- Preset id `presetId` and subtype `presetSubtype` (direction / variant)
|
|
198
|
+
- Duration `duration` and delay `delay` (milliseconds)
|
|
199
|
+
- Optional effect filter `filter` (fade, wipe, barn, …)
|
|
200
|
+
|
|
201
|
+
- Build list `builds` (from `p:bldLst`)
|
|
202
|
+
- Target shape id `spid`
|
|
203
|
+
- Type `type`: `paragraph` | `diagram` | `graphic` | `oleChart`
|
|
204
|
+
- Animate background `animBg`
|
|
205
|
+
|
|
193
206
|
- Slide elements `elements` / master layout elements `layoutElements`
|
|
194
207
|
- OOXML ID `id`
|
|
195
208
|
- Text
|
package/dist/index.cjs
CHANGED
|
@@ -9463,6 +9463,164 @@ function parseTransition(transitionNode) {
|
|
|
9463
9463
|
}
|
|
9464
9464
|
return transition;
|
|
9465
9465
|
}
|
|
9466
|
+
const PRESET_CLASSES = new Set([
|
|
9467
|
+
'entr',
|
|
9468
|
+
'exit',
|
|
9469
|
+
'emph',
|
|
9470
|
+
'path',
|
|
9471
|
+
'verb',
|
|
9472
|
+
'mediacall'
|
|
9473
|
+
]);
|
|
9474
|
+
const NODE_TYPE_TO_TRIGGER = {
|
|
9475
|
+
clickEffect: 'onClick',
|
|
9476
|
+
withEffect: 'withPrevious',
|
|
9477
|
+
afterEffect: 'afterPrevious',
|
|
9478
|
+
clickPar: 'onClick',
|
|
9479
|
+
withGroup: 'withPrevious',
|
|
9480
|
+
afterGroup: 'afterPrevious',
|
|
9481
|
+
interactiveSeq: 'onClick'
|
|
9482
|
+
};
|
|
9483
|
+
function asArray(value) {
|
|
9484
|
+
if (null == value) return [];
|
|
9485
|
+
return Array.isArray(value) ? value : [
|
|
9486
|
+
value
|
|
9487
|
+
];
|
|
9488
|
+
}
|
|
9489
|
+
function parseMs(value) {
|
|
9490
|
+
if (null == value || '' === value || 'indefinite' === value) return null;
|
|
9491
|
+
const ms = parseInt(value, 10);
|
|
9492
|
+
return Number.isFinite(ms) ? ms : null;
|
|
9493
|
+
}
|
|
9494
|
+
function findTimingNode(content) {
|
|
9495
|
+
if (!content) return null;
|
|
9496
|
+
return getTextByPathList(content, [
|
|
9497
|
+
'p:sld',
|
|
9498
|
+
'p:timing'
|
|
9499
|
+
]) || getTextByPathList(content, [
|
|
9500
|
+
'p:sld',
|
|
9501
|
+
'mc:AlternateContent',
|
|
9502
|
+
'mc:Choice',
|
|
9503
|
+
'p:timing'
|
|
9504
|
+
]) || getTextByPathList(content, [
|
|
9505
|
+
'p:sld',
|
|
9506
|
+
'mc:AlternateContent',
|
|
9507
|
+
'mc:Fallback',
|
|
9508
|
+
'p:timing'
|
|
9509
|
+
]);
|
|
9510
|
+
}
|
|
9511
|
+
function collectSpids(node, out = []) {
|
|
9512
|
+
if (!node || 'object' != typeof node) return out;
|
|
9513
|
+
const spid = node.attrs?.spid;
|
|
9514
|
+
if (null != spid && '' !== spid) out.push(String(spid));
|
|
9515
|
+
for (const key of Object.keys(node))if ('attrs' !== key && 'value' !== key) for (const child of asArray(node[key]))collectSpids(child, out);
|
|
9516
|
+
return out;
|
|
9517
|
+
}
|
|
9518
|
+
function collectDuration(node) {
|
|
9519
|
+
let found = null;
|
|
9520
|
+
const walk = (n)=>{
|
|
9521
|
+
if (!n || 'object' != typeof n) return;
|
|
9522
|
+
const ms = parseMs(n.attrs?.dur);
|
|
9523
|
+
if (null != ms && ms >= 0) found = ms;
|
|
9524
|
+
for (const key of Object.keys(n))if ('attrs' !== key && 'value' !== key) for (const child of asArray(n[key]))walk(child);
|
|
9525
|
+
};
|
|
9526
|
+
walk(node);
|
|
9527
|
+
return found;
|
|
9528
|
+
}
|
|
9529
|
+
function collectFilter(node) {
|
|
9530
|
+
let filter = '';
|
|
9531
|
+
const walk = (n)=>{
|
|
9532
|
+
if (!n || 'object' != typeof n) return;
|
|
9533
|
+
if (n.attrs?.filter) filter = n.attrs.filter;
|
|
9534
|
+
for (const key of Object.keys(n))if ('attrs' !== key && 'value' !== key) for (const child of asArray(n[key]))walk(child);
|
|
9535
|
+
};
|
|
9536
|
+
walk(node);
|
|
9537
|
+
return filter;
|
|
9538
|
+
}
|
|
9539
|
+
function collectDelay(node, inherited = 0) {
|
|
9540
|
+
const fromAttr = parseMs(node?.attrs?.delay);
|
|
9541
|
+
if (null != fromAttr && fromAttr >= 0) return fromAttr;
|
|
9542
|
+
const condRoot = node?.['p:stCondLst'];
|
|
9543
|
+
for (const cond of asArray(condRoot?.['p:cond'] || condRoot)){
|
|
9544
|
+
const fromCond = parseMs(cond?.attrs?.delay);
|
|
9545
|
+
if (null != fromCond && fromCond >= 0) return fromCond;
|
|
9546
|
+
}
|
|
9547
|
+
return inherited;
|
|
9548
|
+
}
|
|
9549
|
+
function collectEffects(node, inheritedTrigger = 'onClick', inheritedDelay = 0, results = []) {
|
|
9550
|
+
if (!node || 'object' != typeof node) return results;
|
|
9551
|
+
const attrs = node.attrs || {};
|
|
9552
|
+
const trigger = NODE_TYPE_TO_TRIGGER[attrs.nodeType] || inheritedTrigger;
|
|
9553
|
+
const delay = collectDelay(node, inheritedDelay);
|
|
9554
|
+
if (attrs.presetClass && PRESET_CLASSES.has(attrs.presetClass)) {
|
|
9555
|
+
const spids = [
|
|
9556
|
+
...new Set(collectSpids(node))
|
|
9557
|
+
];
|
|
9558
|
+
const duration = collectDuration(node);
|
|
9559
|
+
const filter = collectFilter(node);
|
|
9560
|
+
const effectTrigger = NODE_TYPE_TO_TRIGGER[attrs.nodeType] || trigger;
|
|
9561
|
+
for (const spid of spids){
|
|
9562
|
+
const animation = {
|
|
9563
|
+
spid,
|
|
9564
|
+
trigger: effectTrigger,
|
|
9565
|
+
class: attrs.presetClass,
|
|
9566
|
+
presetId: parseInt(attrs.presetID || '0', 10) || 0,
|
|
9567
|
+
presetSubtype: parseInt(attrs.presetSubtype || '0', 10) || 0,
|
|
9568
|
+
duration: null != duration ? duration : 1000,
|
|
9569
|
+
delay
|
|
9570
|
+
};
|
|
9571
|
+
if (filter) animation.filter = filter;
|
|
9572
|
+
results.push(animation);
|
|
9573
|
+
}
|
|
9574
|
+
return results;
|
|
9575
|
+
}
|
|
9576
|
+
for (const key of Object.keys(node))if ('attrs' !== key && 'value' !== key) for (const child of asArray(node[key]))collectEffects(child, trigger, delay, results);
|
|
9577
|
+
return results;
|
|
9578
|
+
}
|
|
9579
|
+
function parseBuildList(timing) {
|
|
9580
|
+
const bldLst = timing?.['p:bldLst'];
|
|
9581
|
+
if (!bldLst) return [];
|
|
9582
|
+
const kinds = [
|
|
9583
|
+
[
|
|
9584
|
+
'p:bldP',
|
|
9585
|
+
'paragraph'
|
|
9586
|
+
],
|
|
9587
|
+
[
|
|
9588
|
+
'p:bldDgm',
|
|
9589
|
+
'diagram'
|
|
9590
|
+
],
|
|
9591
|
+
[
|
|
9592
|
+
'p:bldGraphic',
|
|
9593
|
+
'graphic'
|
|
9594
|
+
],
|
|
9595
|
+
[
|
|
9596
|
+
'p:bldOleChart',
|
|
9597
|
+
'oleChart'
|
|
9598
|
+
]
|
|
9599
|
+
];
|
|
9600
|
+
const builds = [];
|
|
9601
|
+
for (const [tag, type] of kinds)for (const node of asArray(bldLst[tag])){
|
|
9602
|
+
const spid = node?.attrs?.spid;
|
|
9603
|
+
if (null == spid || '' === spid) continue;
|
|
9604
|
+
const item = {
|
|
9605
|
+
spid: String(spid),
|
|
9606
|
+
type
|
|
9607
|
+
};
|
|
9608
|
+
if (node.attrs?.animBg === '1' || node.attrs?.animBg === 'true') item.animBg = true;
|
|
9609
|
+
builds.push(item);
|
|
9610
|
+
}
|
|
9611
|
+
return builds;
|
|
9612
|
+
}
|
|
9613
|
+
function parseTiming(slideContent) {
|
|
9614
|
+
const timing = findTimingNode(slideContent);
|
|
9615
|
+
if (!timing) return {
|
|
9616
|
+
animations: [],
|
|
9617
|
+
builds: []
|
|
9618
|
+
};
|
|
9619
|
+
return {
|
|
9620
|
+
animations: collectEffects(timing),
|
|
9621
|
+
builds: parseBuildList(timing)
|
|
9622
|
+
};
|
|
9623
|
+
}
|
|
9466
9624
|
async function loadDiagramFile(warpObj, filename, transformDrawing = false) {
|
|
9467
9625
|
if (!filename) return null;
|
|
9468
9626
|
const cacheKey = `${transformDrawing ? 'drawing:' : 'xml:'}${filename}`;
|
|
@@ -9660,27 +9818,97 @@ async function parse(file, options = {}) {
|
|
|
9660
9818
|
}
|
|
9661
9819
|
};
|
|
9662
9820
|
}
|
|
9821
|
+
function asNodeArray(node) {
|
|
9822
|
+
if (!node) return [];
|
|
9823
|
+
return node.constructor === Array ? node : [
|
|
9824
|
+
node
|
|
9825
|
+
];
|
|
9826
|
+
}
|
|
9827
|
+
function sortSlideXml(p1, p2) {
|
|
9828
|
+
const n1 = +(/(\d+)\.xml/.exec(p1)?.[1] || 0);
|
|
9829
|
+
const n2 = +(/(\d+)\.xml/.exec(p2)?.[1] || 0);
|
|
9830
|
+
return n1 - n2;
|
|
9831
|
+
}
|
|
9832
|
+
function resolvePresentationTarget(target) {
|
|
9833
|
+
let relTarget = String(target || '').replace(/\\/g, '/');
|
|
9834
|
+
if (!relTarget) return '';
|
|
9835
|
+
if (0 === relTarget.indexOf('/ppt/')) return relTarget.substr(1);
|
|
9836
|
+
if (-1 !== relTarget.indexOf('../')) return relTarget.replace('../', 'ppt/');
|
|
9837
|
+
if (0 === relTarget.indexOf('ppt/')) return relTarget;
|
|
9838
|
+
return 'ppt/' + relTarget.replace(/^\//, '');
|
|
9839
|
+
}
|
|
9840
|
+
function relationshipRid(attrs) {
|
|
9841
|
+
if (!attrs) return '';
|
|
9842
|
+
return attrs['r:id'] || attrs.rId || '';
|
|
9843
|
+
}
|
|
9844
|
+
async function getSlidesFromPresentation(zip) {
|
|
9845
|
+
const relsContent = await readXmlFile(zip, 'ppt/_rels/presentation.xml.rels');
|
|
9846
|
+
const relItems = asNodeArray(getTextByPathList(relsContent, [
|
|
9847
|
+
'Relationships',
|
|
9848
|
+
'Relationship'
|
|
9849
|
+
]));
|
|
9850
|
+
const relsMap = {};
|
|
9851
|
+
const slideTargets = [];
|
|
9852
|
+
for (const rel of relItems){
|
|
9853
|
+
const id = getTextByPathList(rel, [
|
|
9854
|
+
'attrs',
|
|
9855
|
+
'Id'
|
|
9856
|
+
]);
|
|
9857
|
+
const type = getTextByPathList(rel, [
|
|
9858
|
+
'attrs',
|
|
9859
|
+
'Type'
|
|
9860
|
+
]);
|
|
9861
|
+
const target = getTextByPathList(rel, [
|
|
9862
|
+
'attrs',
|
|
9863
|
+
'Target'
|
|
9864
|
+
]);
|
|
9865
|
+
if (!id || !target) continue;
|
|
9866
|
+
const loc = resolvePresentationTarget(target);
|
|
9867
|
+
if (loc) {
|
|
9868
|
+
relsMap[id] = loc;
|
|
9869
|
+
if ('slide' === getRelTypeName(type)) slideTargets.push(loc);
|
|
9870
|
+
}
|
|
9871
|
+
}
|
|
9872
|
+
const presentation = await readXmlFile(zip, 'ppt/presentation.xml');
|
|
9873
|
+
const sldIds = asNodeArray(getTextByPathList(presentation, [
|
|
9874
|
+
'p:presentation',
|
|
9875
|
+
'p:sldIdLst',
|
|
9876
|
+
'p:sldId'
|
|
9877
|
+
]));
|
|
9878
|
+
const ordered = [];
|
|
9879
|
+
for (const sldId of sldIds){
|
|
9880
|
+
const rid = relationshipRid(sldId?.attrs);
|
|
9881
|
+
if (rid && relsMap[rid]) ordered.push(relsMap[rid]);
|
|
9882
|
+
}
|
|
9883
|
+
if (ordered.length) return ordered;
|
|
9884
|
+
return slideTargets.sort(sortSlideXml);
|
|
9885
|
+
}
|
|
9663
9886
|
async function getContentTypes(zip) {
|
|
9664
9887
|
const ContentTypesJson = await readXmlFile(zip, '[Content_Types].xml');
|
|
9665
|
-
const
|
|
9888
|
+
const overrides = asNodeArray(getTextByPathList(ContentTypesJson, [
|
|
9889
|
+
'Types',
|
|
9890
|
+
'Override'
|
|
9891
|
+
]));
|
|
9666
9892
|
let slidesLocArray = [];
|
|
9667
9893
|
let slideLayoutsLocArray = [];
|
|
9668
|
-
for (const item of
|
|
9669
|
-
|
|
9670
|
-
|
|
9671
|
-
|
|
9672
|
-
|
|
9673
|
-
|
|
9674
|
-
|
|
9675
|
-
|
|
9894
|
+
for (const item of overrides){
|
|
9895
|
+
const contentType = item?.attrs?.ContentType;
|
|
9896
|
+
const partName = item?.attrs?.PartName;
|
|
9897
|
+
if (!contentType || !partName) continue;
|
|
9898
|
+
const loc = String(partName).replace(/^\//, '');
|
|
9899
|
+
switch(contentType){
|
|
9900
|
+
case 'application/vnd.openxmlformats-officedocument.presentationml.slide+xml':
|
|
9901
|
+
slidesLocArray.push(loc);
|
|
9902
|
+
break;
|
|
9903
|
+
case 'application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml':
|
|
9904
|
+
slideLayoutsLocArray.push(loc);
|
|
9905
|
+
break;
|
|
9906
|
+
default:
|
|
9907
|
+
}
|
|
9676
9908
|
}
|
|
9677
|
-
const sortSlideXml = (p1, p2)=>{
|
|
9678
|
-
const n1 = +/(\d+)\.xml/.exec(p1)[1];
|
|
9679
|
-
const n2 = +/(\d+)\.xml/.exec(p2)[1];
|
|
9680
|
-
return n1 - n2;
|
|
9681
|
-
};
|
|
9682
9909
|
slidesLocArray = slidesLocArray.sort(sortSlideXml);
|
|
9683
9910
|
slideLayoutsLocArray = slideLayoutsLocArray.sort(sortSlideXml);
|
|
9911
|
+
if (!slidesLocArray.length) slidesLocArray = await getSlidesFromPresentation(zip);
|
|
9684
9912
|
return {
|
|
9685
9913
|
slides: slidesLocArray,
|
|
9686
9914
|
slideLayouts: slideLayoutsLocArray
|
|
@@ -9976,6 +10204,7 @@ async function processSingleSlide(zip, sldFileName, themeContent, defaultTextSty
|
|
|
9976
10204
|
if (!transitionNode) transitionNode = findTransitionNode(slideLayoutContent, 'p:sldLayout');
|
|
9977
10205
|
if (!transitionNode) transitionNode = findTransitionNode(slideMasterContent, 'p:sldMaster');
|
|
9978
10206
|
const transition = parseTransition(transitionNode);
|
|
10207
|
+
const { animations, builds } = parseTiming(slideContent);
|
|
9979
10208
|
const showMasterSpOnSlide = getTextByPathList(slideContent, [
|
|
9980
10209
|
'p:sld',
|
|
9981
10210
|
'attrs',
|
|
@@ -9989,7 +10218,9 @@ async function processSingleSlide(zip, sldFileName, themeContent, defaultTextSty
|
|
|
9989
10218
|
layoutElements,
|
|
9990
10219
|
note,
|
|
9991
10220
|
transition,
|
|
9992
|
-
hideBackground
|
|
10221
|
+
hideBackground,
|
|
10222
|
+
animations,
|
|
10223
|
+
builds
|
|
9993
10224
|
};
|
|
9994
10225
|
}
|
|
9995
10226
|
function getHyperlinkFromCNvPr(cNvPr, warpObj) {
|