pptxtojson 1.7.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pptxtojson",
3
- "version": "1.7.0",
3
+ "version": "1.9.0",
4
4
  "description": "A javascript tool for parsing .pptx file",
5
5
  "type": "module",
6
6
  "main": "./dist/index.umd.js",
package/src/align.js CHANGED
@@ -62,4 +62,38 @@ export function getVerticalAlign(node, slideLayoutSpNode, slideMasterSpNode) {
62
62
  }
63
63
  }
64
64
  return (anchor === 'ctr') ? 'mid' : ((anchor === 'b') ? 'down' : 'up')
65
+ }
66
+
67
+ export function getTextAutoFit(node, slideLayoutSpNode, slideMasterSpNode) {
68
+ function checkBodyPr(bodyPr) {
69
+ if (!bodyPr) return null
70
+
71
+ if (bodyPr['a:noAutofit']) return { result: null }
72
+ else if (bodyPr['a:spAutoFit']) return { result: { type: 'shape' } }
73
+ else if (bodyPr['a:normAutofit']) {
74
+ const fontScale = getTextByPathList(bodyPr['a:normAutofit'], ['attrs', 'fontScale'])
75
+ if (fontScale) {
76
+ const scalePercent = parseInt(fontScale) / 1000
77
+ return {
78
+ result: {
79
+ type: 'text',
80
+ fontScale: scalePercent,
81
+ }
82
+ }
83
+ }
84
+ return { result: { type: 'text' } }
85
+ }
86
+ return null
87
+ }
88
+
89
+ const nodeCheck = checkBodyPr(getTextByPathList(node, ['p:txBody', 'a:bodyPr']))
90
+ if (nodeCheck) return nodeCheck.result
91
+
92
+ const layoutCheck = checkBodyPr(getTextByPathList(slideLayoutSpNode, ['p:txBody', 'a:bodyPr']))
93
+ if (layoutCheck) return layoutCheck.result
94
+
95
+ const masterCheck = checkBodyPr(getTextByPathList(slideMasterSpNode, ['p:txBody', 'a:bodyPr']))
96
+ if (masterCheck) return masterCheck.result
97
+
98
+ return null
65
99
  }
package/src/fill.js CHANGED
@@ -180,7 +180,7 @@ export function getGradientFill(node, warpObj) {
180
180
  for (let i = 0; i < gsLst.length; i++) {
181
181
  const lo_color = getSolidFill(gsLst[i], undefined, undefined, warpObj)
182
182
  const pos = getTextByPathList(gsLst[i], ['attrs', 'pos'])
183
-
183
+
184
184
  colors[i] = {
185
185
  pos: pos ? (pos / 1000 + '%') : '',
186
186
  color: lo_color,
@@ -192,7 +192,7 @@ export function getGradientFill(node, warpObj) {
192
192
  if (lin) rot = angleToDegrees(lin['attrs']['ang'])
193
193
  else {
194
194
  const path = node['a:path']
195
- if (path && path['attrs'] && path['attrs']['path']) pathType = path['attrs']['path']
195
+ if (path && path['attrs'] && path['attrs']['path']) pathType = path['attrs']['path']
196
196
  }
197
197
  return {
198
198
  rot,
@@ -201,6 +201,35 @@ export function getGradientFill(node, warpObj) {
201
201
  }
202
202
  }
203
203
 
204
+ export function getPatternFill(node, warpObj) {
205
+ if (!node) return null
206
+
207
+ const pattFill = node['a:pattFill']
208
+ if (!pattFill) return null
209
+
210
+ const type = getTextByPathList(pattFill, ['attrs', 'prst'])
211
+
212
+ const fgColorNode = pattFill['a:fgClr']
213
+ const bgColorNode = pattFill['a:bgClr']
214
+
215
+ let foregroundColor = '#000000'
216
+ let backgroundColor = '#FFFFFF'
217
+
218
+ if (fgColorNode) {
219
+ foregroundColor = getSolidFill(fgColorNode, undefined, undefined, warpObj)
220
+ }
221
+
222
+ if (bgColorNode) {
223
+ backgroundColor = getSolidFill(bgColorNode, undefined, undefined, warpObj)
224
+ }
225
+
226
+ return {
227
+ type,
228
+ foregroundColor,
229
+ backgroundColor,
230
+ }
231
+ }
232
+
204
233
  export function getBgGradientFill(bgPr, phClr, slideMasterContent, warpObj) {
205
234
  if (bgPr) {
206
235
  const grdFill = bgPr['a:gradFill']
@@ -277,6 +306,13 @@ export async function getSlideBackgroundFill(warpObj) {
277
306
  background = await getBgPicFill(bgPr, 'slideBg', warpObj)
278
307
  backgroundType = 'image'
279
308
  }
309
+ else if (bgFillTyp === 'PATTERN_FILL') {
310
+ const patternFill = getPatternFill(bgPr, warpObj)
311
+ if (patternFill) {
312
+ background = patternFill
313
+ backgroundType = 'pattern'
314
+ }
315
+ }
280
316
  }
281
317
  else if (bgRef) {
282
318
  let clrMapOvr
@@ -374,6 +410,13 @@ export async function getSlideBackgroundFill(warpObj) {
374
410
  background = await getBgPicFill(bgPr, 'slideLayoutBg', warpObj)
375
411
  backgroundType = 'image'
376
412
  }
413
+ else if (bgFillTyp === 'PATTERN_FILL') {
414
+ const patternFill = getPatternFill(bgPr, warpObj)
415
+ if (patternFill) {
416
+ background = patternFill
417
+ backgroundType = 'pattern'
418
+ }
419
+ }
377
420
  }
378
421
  else if (bgRef) {
379
422
  const phClr = getSolidFill(bgRef, clrMapOvr, undefined, warpObj)
@@ -420,7 +463,7 @@ export async function getSlideBackgroundFill(warpObj) {
420
463
  const sldFill = bgFillLstIdx['a:solidFill']
421
464
  const sldBgClr = getSolidFill(sldFill, clrMapOvr, undefined, warpObj)
422
465
  background = sldBgClr
423
- }
466
+ }
424
467
  else if (bgFillTyp === 'GRADIENT_FILL') {
425
468
  const gradientFill = getBgGradientFill(bgFillLstIdx, phClr, slideMasterContent, warpObj)
426
469
  if (typeof gradientFill === 'string') {
@@ -435,6 +478,13 @@ export async function getSlideBackgroundFill(warpObj) {
435
478
  background = await getBgPicFill(bgFillLstIdx, 'themeBg', warpObj)
436
479
  backgroundType = 'image'
437
480
  }
481
+ else if (bgFillTyp === 'PATTERN_FILL') {
482
+ const patternFill = getPatternFill(bgFillLstIdx, warpObj)
483
+ if (patternFill) {
484
+ background = patternFill
485
+ backgroundType = 'pattern'
486
+ }
487
+ }
438
488
  }
439
489
  }
440
490
  else {
@@ -463,6 +513,13 @@ export async function getSlideBackgroundFill(warpObj) {
463
513
  background = await getBgPicFill(bgPr, 'slideMasterBg', warpObj)
464
514
  backgroundType = 'image'
465
515
  }
516
+ else if (bgFillTyp === 'PATTERN_FILL') {
517
+ const patternFill = getPatternFill(bgPr, warpObj)
518
+ if (patternFill) {
519
+ background = patternFill
520
+ backgroundType = 'pattern'
521
+ }
522
+ }
466
523
  }
467
524
  else if (bgRef) {
468
525
  const phClr = getSolidFill(bgRef, clrMap, undefined, warpObj)
@@ -509,7 +566,7 @@ export async function getSlideBackgroundFill(warpObj) {
509
566
  const sldFill = bgFillLstIdx['a:solidFill']
510
567
  const sldBgClr = getSolidFill(sldFill, clrMapOvr, undefined, warpObj)
511
568
  background = sldBgClr
512
- }
569
+ }
513
570
  else if (bgFillTyp === 'GRADIENT_FILL') {
514
571
  const gradientFill = getBgGradientFill(bgFillLstIdx, phClr, slideMasterContent, warpObj)
515
572
  if (typeof gradientFill === 'string') {
@@ -524,6 +581,13 @@ export async function getSlideBackgroundFill(warpObj) {
524
581
  background = await getBgPicFill(bgFillLstIdx, 'themeBg', warpObj)
525
582
  backgroundType = 'image'
526
583
  }
584
+ else if (bgFillTyp === 'PATTERN_FILL') {
585
+ const patternFill = getPatternFill(bgFillLstIdx, warpObj)
586
+ if (patternFill) {
587
+ background = patternFill
588
+ backgroundType = 'pattern'
589
+ }
590
+ }
527
591
  }
528
592
  }
529
593
  }
@@ -561,6 +625,11 @@ export async function getShapeFill(node, pNode, isSvgMode, warpObj, source, grou
561
625
  }
562
626
  type = 'image'
563
627
  }
628
+ else if (fillType === 'PATTERN_FILL') {
629
+ const shpFill = node['p:spPr']['a:pattFill']
630
+ fillValue = getPatternFill({ 'a:pattFill': shpFill }, warpObj)
631
+ type = 'pattern'
632
+ }
564
633
  else if (fillType === 'GROUP_FILL') {
565
634
  return findFillInGroupHierarchy(groupHierarchy, warpObj, source)
566
635
  }
@@ -620,6 +689,16 @@ async function findFillInGroupHierarchy(groupHierarchy, warpObj, source) {
620
689
  }
621
690
  }
622
691
  }
692
+ else if (fillType === 'PATTERN_FILL') {
693
+ const shpFill = grpSpPr['a:pattFill']
694
+ const fillValue = getPatternFill({ 'a:pattFill': shpFill }, warpObj)
695
+ if (fillValue) {
696
+ return {
697
+ type: 'pattern',
698
+ value: fillValue,
699
+ }
700
+ }
701
+ }
623
702
  }
624
703
 
625
704
  return null
package/src/fontStyle.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { getTextByPathList } from './utils'
2
2
  import { getShadow } from './shadow'
3
- import { getFillType, getSolidFill } from './fill'
3
+ import { getFillType, getGradientFill, getSolidFill } from './fill'
4
4
 
5
5
  export function getFontType(node, type, warpObj) {
6
6
  let typeface = getTextByPathList(node, ['a:rPr', 'a:latin', 'attrs', 'typeface'])
@@ -31,6 +31,11 @@ export function getFontColor(node, pNode, lstStyle, pFontStyle, lvl, warpObj) {
31
31
  const solidFillNode = rPrNode['a:solidFill']
32
32
  color = getSolidFill(solidFillNode, undefined, undefined, warpObj)
33
33
  }
34
+ if (filTyp === 'GRADIENT_FILL') {
35
+ const gradientFillNode = rPrNode['a:gradFill']
36
+ const gradient = getGradientFill(gradientFillNode, warpObj)
37
+ return gradient
38
+ }
34
39
  }
35
40
  if (!color && getTextByPathList(lstStyle, ['a:lvl' + lvl + 'pPr', 'a:defRPr'])) {
36
41
  const lstStyledefRPr = getTextByPathList(lstStyle, ['a:lvl' + lvl + 'pPr', 'a:defRPr'])
package/src/position.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { RATIO_EMUs_Points } from './constants'
2
+ import { numberToFixed } from './utils'
2
3
 
3
4
  export function getPosition(slideSpNode, slideLayoutSpNode, slideMasterSpNode) {
4
5
  let off
@@ -10,8 +11,8 @@ export function getPosition(slideSpNode, slideLayoutSpNode, slideMasterSpNode) {
10
11
  if (!off) return { top: 0, left: 0 }
11
12
 
12
13
  return {
13
- top: parseInt(off['y']) * RATIO_EMUs_Points,
14
- left: parseInt(off['x']) * RATIO_EMUs_Points,
14
+ top: numberToFixed(parseInt(off['y']) * RATIO_EMUs_Points),
15
+ left: numberToFixed(parseInt(off['x']) * RATIO_EMUs_Points),
15
16
  }
16
17
  }
17
18
 
@@ -25,7 +26,7 @@ export function getSize(slideSpNode, slideLayoutSpNode, slideMasterSpNode) {
25
26
  if (!ext) return { width: 0, height: 0 }
26
27
 
27
28
  return {
28
- width: parseInt(ext['cx']) * RATIO_EMUs_Points,
29
- height: parseInt(ext['cy']) * RATIO_EMUs_Points,
29
+ width: numberToFixed(parseInt(ext['cx']) * RATIO_EMUs_Points),
30
+ height: numberToFixed(parseInt(ext['cy']) * RATIO_EMUs_Points),
30
31
  }
31
32
  }
package/src/pptxtojson.js CHANGED
@@ -3,11 +3,11 @@ import { readXmlFile } from './readXmlFile'
3
3
  import { getBorder } from './border'
4
4
  import { getSlideBackgroundFill, getShapeFill, getSolidFill, getPicFill, getPicFilters } from './fill'
5
5
  import { getChartInfo } from './chart'
6
- import { getVerticalAlign } from './align'
6
+ import { getVerticalAlign, getTextAutoFit } from './align'
7
7
  import { getPosition, getSize } from './position'
8
8
  import { genTextBody } from './text'
9
9
  import { getCustomShapePath } from './shape'
10
- import { extractFileExtension, base64ArrayBuffer, getTextByPathList, angleToDegrees, getMimeType, isVideoLink, escapeHtml, hasValidText } from './utils'
10
+ import { extractFileExtension, base64ArrayBuffer, getTextByPathList, angleToDegrees, getMimeType, isVideoLink, escapeHtml, hasValidText, numberToFixed } from './utils'
11
11
  import { getShadow } from './shadow'
12
12
  import { getTableBorders, getTableCellParams, getTableRowParams } from './table'
13
13
  import { RATIO_EMUs_Points } from './constants'
@@ -516,20 +516,20 @@ async function processGroupSpNode(node, warpObj, source, parentGroupHierarchy =
516
516
 
517
517
  return {
518
518
  type: 'group',
519
- top: y,
520
- left: x,
521
- width: cx,
522
- height: cy,
519
+ top: numberToFixed(y),
520
+ left: numberToFixed(x),
521
+ width: numberToFixed(cx),
522
+ height: numberToFixed(cy),
523
523
  rotate,
524
524
  order,
525
525
  isFlipV,
526
526
  isFlipH,
527
527
  elements: elements.map(element => ({
528
528
  ...element,
529
- left: (element.left - chx) * ws,
530
- top: (element.top - chy) * hs,
531
- width: element.width * ws,
532
- height: element.height * hs,
529
+ left: numberToFixed((element.left - chx) * ws),
530
+ top: numberToFixed((element.top - chy) * hs),
531
+ width: numberToFixed(element.width * ws),
532
+ height: numberToFixed(element.height * hs),
533
533
  }))
534
534
  }
535
535
  }
@@ -617,6 +617,7 @@ async function genShape(node, pNode, slideLayoutSpNode, slideMasterSpNode, name,
617
617
 
618
618
  const vAlign = getVerticalAlign(node, slideLayoutSpNode, slideMasterSpNode, type)
619
619
  const isVertical = getTextByPathList(node, ['p:txBody', 'a:bodyPr', 'attrs', 'vert']) === 'eaVert'
620
+ const autoFit = getTextAutoFit(node, slideLayoutSpNode, slideMasterSpNode)
620
621
 
621
622
  const data = {
622
623
  left,
@@ -638,6 +639,7 @@ async function genShape(node, pNode, slideLayoutSpNode, slideMasterSpNode, name,
638
639
  }
639
640
 
640
641
  if (shadow) data.shadow = shadow
642
+ if (autoFit) data.autoFit = autoFit
641
643
 
642
644
  const isHasValidText = data.content && hasValidText(data.content)
643
645
 
package/src/text.js CHANGED
@@ -73,10 +73,39 @@ export function genTextBody(textBodyNode, spNode, slideLayoutSpNode, type, warpO
73
73
  text += `<p style="text-align: ${align};">`
74
74
  }
75
75
 
76
- if (!rNode) text += genSpanElement(pNode, spNode, textBodyNode, pFontStyle, slideLayoutSpNode, type, warpObj)
76
+ if (!rNode) {
77
+ text += genSpanElement(pNode, spNode, textBodyNode, pFontStyle, slideLayoutSpNode, type, warpObj)
78
+ }
77
79
  else {
80
+ let prevStyleInfo = null
81
+ let accumulatedText = ''
82
+
78
83
  for (const rNodeItem of rNode) {
79
- text += genSpanElement(rNodeItem, pNode, textBodyNode, pFontStyle, slideLayoutSpNode, type, warpObj)
84
+ const styleInfo = getSpanStyleInfo(rNodeItem, pNode, textBodyNode, pFontStyle, slideLayoutSpNode, type, warpObj)
85
+
86
+ if (!prevStyleInfo || prevStyleInfo.styleText !== styleInfo.styleText || prevStyleInfo.hasLink !== styleInfo.hasLink || styleInfo.hasLink) {
87
+ if (accumulatedText) {
88
+ const processedText = accumulatedText.replace(/\t/g, '&nbsp;&nbsp;&nbsp;&nbsp;').replace(/\s/g, '&nbsp;')
89
+ text += `<span style="${prevStyleInfo.styleText}">${processedText}</span>`
90
+ accumulatedText = ''
91
+ }
92
+
93
+ if (styleInfo.hasLink) {
94
+ const processedText = styleInfo.text.replace(/\t/g, '&nbsp;&nbsp;&nbsp;&nbsp;').replace(/\s/g, '&nbsp;')
95
+ text += `<span style="${styleInfo.styleText}"><a href="${styleInfo.linkURL}" target="_blank">${processedText}</a></span>`
96
+ prevStyleInfo = null
97
+ }
98
+ else {
99
+ prevStyleInfo = styleInfo
100
+ accumulatedText = styleInfo.text
101
+ }
102
+ }
103
+ else accumulatedText += styleInfo.text
104
+ }
105
+
106
+ if (accumulatedText && prevStyleInfo) {
107
+ const processedText = accumulatedText.replace(/\t/g, '&nbsp;&nbsp;&nbsp;&nbsp;').replace(/\s/g, '&nbsp;')
108
+ text += `<span style="${prevStyleInfo.styleText}">${processedText}</span>`
80
109
  }
81
110
  }
82
111
 
@@ -97,6 +126,16 @@ export function getListType(node) {
97
126
  }
98
127
 
99
128
  export function genSpanElement(node, pNode, textBodyNode, pFontStyle, slideLayoutSpNode, type, warpObj) {
129
+ const { styleText, text, hasLink, linkURL } = getSpanStyleInfo(node, pNode, textBodyNode, pFontStyle, slideLayoutSpNode, type, warpObj)
130
+ const processedText = text.replace(/\t/g, '&nbsp;&nbsp;&nbsp;&nbsp;').replace(/\s/g, '&nbsp;')
131
+
132
+ if (hasLink) {
133
+ return `<span style="${styleText}"><a href="${linkURL}" target="_blank">${processedText}</a></span>`
134
+ }
135
+ return `<span style="${styleText}">${processedText}</span>`
136
+ }
137
+
138
+ export function getSpanStyleInfo(node, pNode, textBodyNode, pFontStyle, slideLayoutSpNode, type, warpObj) {
100
139
  const lstStyle = textBodyNode['a:lstStyle']
101
140
  const slideMasterTextStyles = warpObj['slideMasterTextStyles']
102
141
 
@@ -121,7 +160,15 @@ export function genSpanElement(node, pNode, textBodyNode, pFontStyle, slideLayou
121
160
  const shadow = getFontShadow(node, warpObj)
122
161
  const subscript = getFontSubscript(node)
123
162
 
124
- if (fontColor) styleText += `color: ${fontColor};`
163
+ if (fontColor) {
164
+ if (typeof fontColor === 'string') styleText += `color: ${fontColor};`
165
+ else if (fontColor.colors) {
166
+ const { colors, rot } = fontColor
167
+ const stops = colors.map(item => `${item.color} ${item.pos}`).join(', ')
168
+ const gradientStyle = `linear-gradient(${rot + 90}deg, ${stops})`
169
+ styleText += `background: ${gradientStyle}; background-clip: text; color: transparent;`
170
+ }
171
+ }
125
172
  if (fontSize) styleText += `font-size: ${fontSize};`
126
173
  if (fontType) styleText += `font-family: ${fontType};`
127
174
  if (fontBold) styleText += `font-weight: ${fontBold};`
@@ -133,9 +180,12 @@ export function genSpanElement(node, pNode, textBodyNode, pFontStyle, slideLayou
133
180
  if (shadow) styleText += `text-shadow: ${shadow};`
134
181
 
135
182
  const linkID = getTextByPathList(node, ['a:rPr', 'a:hlinkClick', 'attrs', 'r:id'])
136
- if (linkID && warpObj['slideResObj'][linkID]) {
137
- const linkURL = warpObj['slideResObj'][linkID]['target']
138
- return `<span style="${styleText}"><a href="${linkURL}" target="_blank">${text.replace(/\t/g, '&nbsp;&nbsp;&nbsp;&nbsp;').replace(/\s/g, '&nbsp;')}</a></span>`
139
- }
140
- return `<span style="${styleText}">${text.replace(/\t/g, '&nbsp;&nbsp;&nbsp;&nbsp;').replace(/\s/g, '&nbsp;')}</span>`
183
+ const hasLink = linkID && warpObj['slideResObj'][linkID]
184
+
185
+ return {
186
+ styleText,
187
+ text,
188
+ hasLink,
189
+ linkURL: hasLink ? warpObj['slideResObj'][linkID]['target'] : null
190
+ }
141
191
  }
package/src/utils.js CHANGED
@@ -160,4 +160,8 @@ export function hasValidText(htmlString) {
160
160
  const doc = parser.parseFromString(htmlString, 'text/html')
161
161
  const text = doc.body.textContent || doc.body.innerText
162
162
  return text.trim() !== ''
163
+ }
164
+
165
+ export function numberToFixed(num, fractionDigits = 4) {
166
+ return parseFloat(num.toFixed(fractionDigits))
163
167
  }