pptxtojson 2.0.6 → 2.2.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": "2.0.6",
3
+ "version": "2.2.0",
4
4
  "description": "A javascript tool for parsing .pptx file",
5
5
  "type": "module",
6
6
  "main": "./dist/index.umd.js",
package/src/border.js CHANGED
@@ -1,8 +1,19 @@
1
- import tinycolor from 'tinycolor2'
2
- import { getSchemeColorFromTheme } from './schemeColor'
3
- import { getTextByPathList } from './utils'
4
-
5
- export function getBorder(node, elType, warpObj) {
1
+ import tinycolor from 'tinycolor2'
2
+ import { getSolidFill } from './fill'
3
+ import { getSchemeColorFromTheme } from './schemeColor'
4
+ import { getTextByPathList } from './utils'
5
+
6
+ function getLineEnd(node) {
7
+ const attrs = getTextByPathList(node, ['attrs'])
8
+ if (!attrs) return undefined
9
+
10
+ const lineEnd = { type: attrs.type || 'none' }
11
+ if (attrs.w) lineEnd.width = attrs.w
12
+ if (attrs.len) lineEnd.length = attrs.len
13
+ return lineEnd
14
+ }
15
+
16
+ export function getBorder(node, elType, warpObj) {
6
17
  let lineNode = getTextByPathList(node, ['p:spPr', 'a:ln'])
7
18
  if (!lineNode) {
8
19
  const lnRefNode = getTextByPathList(node, ['p:style', 'a:lnRef'])
@@ -22,12 +33,8 @@ export function getBorder(node, elType, warpObj) {
22
33
  else borderWidth = 1
23
34
  }
24
35
 
25
- let borderColor = getTextByPathList(lineNode, ['a:solidFill', 'a:srgbClr', 'attrs', 'val'])
26
- if (!borderColor) {
27
- const schemeClrNode = getTextByPathList(lineNode, ['a:solidFill', 'a:schemeClr'])
28
- const schemeClr = 'a:' + getTextByPathList(schemeClrNode, ['attrs', 'val'])
29
- borderColor = getSchemeColorFromTheme(schemeClr, warpObj)
30
- }
36
+ const solidFill = getTextByPathList(lineNode, ['a:solidFill'])
37
+ let borderColor = getSolidFill(solidFill, undefined, undefined, warpObj)
31
38
 
32
39
  if (!borderColor) {
33
40
  const schemeClrNode = getTextByPathList(node, ['p:style', 'a:lnRef', 'a:schemeClr'])
@@ -44,15 +51,15 @@ export function getBorder(node, elType, warpObj) {
44
51
  borderColor = tinycolor({ h: color.h, s: color.s, l: color.l * shade, a: color.a }).toHex()
45
52
  }
46
53
  }
47
- }
48
-
49
- if (!borderColor) borderColor = '#000000'
50
- else borderColor = `#${borderColor}`
54
+ }
55
+
56
+ if (!borderColor) borderColor = '#000000'
57
+ else if (!borderColor.startsWith('#')) borderColor = `#${borderColor}`
51
58
 
52
59
  const type = getTextByPathList(lineNode, ['a:prstDash', 'attrs', 'val'])
53
60
  let borderType = 'solid'
54
61
  let strokeDasharray = '0'
55
- switch (type) {
62
+ switch (type) {
56
63
  case 'solid':
57
64
  borderType = 'solid'
58
65
  strokeDasharray = '0'
@@ -93,13 +100,18 @@ export function getBorder(node, elType, warpObj) {
93
100
  borderType = 'dotted'
94
101
  strokeDasharray = '2, 5'
95
102
  break
96
- default:
97
- }
98
-
99
- return {
100
- borderColor,
101
- borderWidth,
102
- borderType,
103
- strokeDasharray,
104
- }
105
- }
103
+ default:
104
+ }
105
+
106
+ const headEnd = getLineEnd(getTextByPathList(lineNode, ['a:headEnd']))
107
+ const tailEnd = getLineEnd(getTextByPathList(lineNode, ['a:tailEnd']))
108
+
109
+ return {
110
+ borderColor,
111
+ borderWidth,
112
+ borderType,
113
+ strokeDasharray,
114
+ headEnd,
115
+ tailEnd,
116
+ }
117
+ }
package/src/diagram.js CHANGED
@@ -67,10 +67,12 @@ export async function getDiagramNodeContext(node, warpObj) {
67
67
  let relationshipArray = digramResContent['Relationships']['Relationship']
68
68
  if (relationshipArray && relationshipArray.constructor !== Array) relationshipArray = [relationshipArray]
69
69
  if (relationshipArray) {
70
- for (const relationshipArrayItem of relationshipArray) {
71
- let relTarget = relationshipArrayItem['attrs']['Target']
72
- if (relTarget.indexOf('../') !== -1) relTarget = relTarget.replace('../', 'ppt/')
73
- else relTarget = drawingTarget.replace(drawingName, '') + relTarget
70
+ for (const relationshipArrayItem of relationshipArray) {
71
+ let relTarget = relationshipArrayItem['attrs']['Target']
72
+ relTarget = relTarget.replace(/\\/g, '/')
73
+ if (relTarget.indexOf('/ppt/') === 0) relTarget = relTarget.substr(1)
74
+ else if (relTarget.indexOf('../') !== -1) relTarget = relTarget.replace('../', 'ppt/')
75
+ else relTarget = drawingTarget.replace(drawingName, '') + relTarget
74
76
 
75
77
  diagramResObj[relationshipArrayItem['attrs']['Id']] = {
76
78
  type: relationshipArrayItem['attrs']['Type'].replace('http://schemas.openxmlformats.org/officeDocument/2006/relationships/', ''),
@@ -129,4 +131,4 @@ export function getSmartArtTextData(dataContent) {
129
131
  }
130
132
 
131
133
  return result
132
- }
134
+ }
package/src/paragraph.js CHANGED
@@ -177,12 +177,16 @@ function getParagraphStyleNodes(pNode, textBodyNode, slideLayoutSpNode, slideMas
177
177
  return styleNodes
178
178
  }
179
179
 
180
- function getLineSpacingValue(spacingNode) {
181
- const spcPct = getTextByPathList(spacingNode, ['a:spcPct', 'attrs', 'val'])
182
- const spcPts = getTextByPathList(spacingNode, ['a:spcPts', 'attrs', 'val'])
183
-
184
- if (spcPct) return parseInt(spcPct) / 1000 / 100
185
- if (spcPts) return parseInt(spcPts) / 100 + 'pt'
180
+ function getLineSpacingValue(spacingNode, singleLineSpacingFactor = 1) {
181
+ const spcPct = getTextByPathList(spacingNode, ['a:spcPct', 'attrs', 'val'])
182
+ const spcPts = getTextByPathList(spacingNode, ['a:spcPts', 'attrs', 'val'])
183
+
184
+ if (spcPct !== undefined) {
185
+ const value = String(spcPct).trim()
186
+ const percentage = value.endsWith('%') ? Number.parseFloat(value) / 100 : Number.parseInt(value, 10) / 100000
187
+ if (Number.isFinite(percentage)) return percentage * singleLineSpacingFactor
188
+ }
189
+ if (spcPts) return parseInt(spcPts) / 100 + 'pt'
186
190
 
187
191
  return undefined
188
192
  }
@@ -205,16 +209,17 @@ function getParagraphIndentValue(styleNode, attrName) {
205
209
  return undefined
206
210
  }
207
211
 
208
- export function getParagraphSpacing(pNode, textBodyNode, slideLayoutSpNode, slideMasterSpNode, type, slideMasterTextStyles, warpObj) {
209
- const styleNodes = getParagraphStyleNodes(pNode, textBodyNode, slideLayoutSpNode, slideMasterSpNode, type, slideMasterTextStyles, warpObj)
210
- if (!styleNodes) return null
211
-
212
- const spacing = {}
213
-
214
- for (const styleNode of styleNodes) {
215
- if (spacing.lineSpacing === undefined) {
216
- const lineSpacing = getLineSpacingValue(styleNode['a:lnSpc'])
217
- if (lineSpacing !== undefined) spacing.lineSpacing = lineSpacing
212
+ export function getParagraphSpacing(pNode, textBodyNode, slideLayoutSpNode, slideMasterSpNode, type, slideMasterTextStyles, warpObj) {
213
+ const styleNodes = getParagraphStyleNodes(pNode, textBodyNode, slideLayoutSpNode, slideMasterSpNode, type, slideMasterTextStyles, warpObj)
214
+ if (!styleNodes) return null
215
+
216
+ const spacing = {}
217
+ const singleLineSpacingFactor = getTextByPathList(warpObj, ['options', 'singleLineSpacingFactor'])
218
+
219
+ for (const styleNode of styleNodes) {
220
+ if (spacing.lineSpacing === undefined) {
221
+ const lineSpacing = getLineSpacingValue(styleNode['a:lnSpc'], singleLineSpacingFactor)
222
+ if (lineSpacing !== undefined) spacing.lineSpacing = lineSpacing
218
223
  }
219
224
 
220
225
  if (spacing.spaceBefore === undefined) {
package/src/pptxtojson.js CHANGED
@@ -25,10 +25,11 @@ export async function parse(file, options = {}) {
25
25
  const xmlCache = {}
26
26
  const parseOptions = {
27
27
  ...options,
28
- imageMode: options.imageMode || 'base64',
29
- videoMode: options.videoMode || 'none',
30
- audioMode: options.audioMode || 'none',
31
- }
28
+ imageMode: options.imageMode || 'base64',
29
+ videoMode: options.videoMode || 'none',
30
+ audioMode: options.audioMode || 'none',
31
+ singleLineSpacingFactor: Number.isFinite(options.singleLineSpacingFactor) && options.singleLineSpacingFactor > 0 ? options.singleLineSpacingFactor : 1,
32
+ }
32
33
 
33
34
  const zip = await JSZip.loadAsync(file)
34
35
 
@@ -129,7 +130,12 @@ async function getTheme(zip) {
129
130
  themeURI = relationshipArray['attrs']['Target']
130
131
  }
131
132
 
132
- const themeContent = await readXmlFile(zip, 'ppt/' + themeURI)
133
+ let themeContent = null
134
+ if (themeURI) {
135
+ themeURI = themeURI.replace(/\\/g, '/')
136
+ const themeFilename = themeURI.indexOf('/ppt/') === 0 ? themeURI.substr(1) : 'ppt/' + themeURI
137
+ themeContent = await readXmlFile(zip, themeFilename)
138
+ }
133
139
 
134
140
  const themeColors = []
135
141
  const clrScheme = getTextByPathList(themeContent, ['a:theme', 'a:themeElements', 'a:clrScheme'])
@@ -173,7 +179,9 @@ async function processSingleSlide(zip, sldFileName, themeContent, defaultTextSty
173
179
  let relTarget = relationshipArrayItem['attrs']['Target']
174
180
  const isExternal = relationshipArrayItem['attrs']['TargetMode'] === 'External'
175
181
  if (!isExternal) {
176
- if (relTarget.indexOf('../') !== -1) relTarget = relTarget.replace('../', 'ppt/')
182
+ relTarget = relTarget.replace(/\\/g, '/')
183
+ if (relTarget.indexOf('/ppt/') === 0) relTarget = relTarget.substr(1)
184
+ else if (relTarget.indexOf('../') !== -1) relTarget = relTarget.replace('../', 'ppt/')
177
185
  else relTarget = 'ppt/slides/' + relTarget
178
186
  }
179
187
 
@@ -226,7 +234,9 @@ async function processSingleSlide(zip, sldFileName, themeContent, defaultTextSty
226
234
  for (const relationshipArrayItem of relationshipArray) {
227
235
  const relType = relationshipArrayItem['attrs']['Type'].replace('http://schemas.openxmlformats.org/officeDocument/2006/relationships/', '')
228
236
  let relTarget = relationshipArrayItem['attrs']['Target']
229
- if (relTarget.indexOf('../') !== -1) relTarget = relTarget.replace('../', 'ppt/')
237
+ relTarget = relTarget.replace(/\\/g, '/')
238
+ if (relTarget.indexOf('/ppt/') === 0) relTarget = relTarget.substr(1)
239
+ else if (relTarget.indexOf('../') !== -1) relTarget = relTarget.replace('../', 'ppt/')
230
240
  else relTarget = 'ppt/slideLayouts/' + relTarget
231
241
 
232
242
  switch (relationshipArrayItem['attrs']['Type']) {
@@ -252,7 +262,9 @@ async function processSingleSlide(zip, sldFileName, themeContent, defaultTextSty
252
262
  for (const relationshipArrayItem of relationshipArray) {
253
263
  const relType = relationshipArrayItem['attrs']['Type'].replace('http://schemas.openxmlformats.org/officeDocument/2006/relationships/', '')
254
264
  let relTarget = relationshipArrayItem['attrs']['Target']
255
- if (relTarget.indexOf('../') !== -1) relTarget = relTarget.replace('../', 'ppt/')
265
+ relTarget = relTarget.replace(/\\/g, '/')
266
+ if (relTarget.indexOf('/ppt/') === 0) relTarget = relTarget.substr(1)
267
+ else if (relTarget.indexOf('../') !== -1) relTarget = relTarget.replace('../', 'ppt/')
256
268
  else relTarget = 'ppt/slideMasters/' + relTarget
257
269
 
258
270
  switch (relationshipArrayItem['attrs']['Type']) {
@@ -267,10 +279,10 @@ async function processSingleSlide(zip, sldFileName, themeContent, defaultTextSty
267
279
  }
268
280
  }
269
281
 
270
- let currentThemeContent = themeContent
271
- if (themeFilename) {
272
- currentThemeContent = currentThemeContent || await readXmlFileCached(zip, themeFilename, xmlCache)
273
- const themeName = themeFilename.split('/').pop()
282
+ let currentThemeContent = themeContent
283
+ if (themeFilename) {
284
+ currentThemeContent = await readXmlFileCached(zip, themeFilename, xmlCache) || currentThemeContent
285
+ const themeName = themeFilename.split('/').pop()
274
286
  const themeResFileName = themeFilename.replace(themeName, '_rels/' + themeName) + '.rels'
275
287
  const themeResContent = await readXmlFile(zip, themeResFileName)
276
288
  if (themeResContent) {
@@ -278,9 +290,14 @@ async function processSingleSlide(zip, sldFileName, themeContent, defaultTextSty
278
290
  if (relationshipArray) {
279
291
  if (relationshipArray.constructor !== Array) relationshipArray = [relationshipArray]
280
292
  for (const relationshipArrayItem of relationshipArray) {
293
+ let relTarget = relationshipArrayItem['attrs']['Target']
294
+ relTarget = relTarget.replace(/\\/g, '/')
295
+ if (relTarget.indexOf('/ppt/') === 0) relTarget = relTarget.substr(1)
296
+ else relTarget = relTarget.replace('../', 'ppt/')
297
+
281
298
  themeResObj[relationshipArrayItem['attrs']['Id']] = {
282
299
  'type': relationshipArrayItem['attrs']['Type'].replace('http://schemas.openxmlformats.org/officeDocument/2006/relationships/', ''),
283
- 'target': relationshipArrayItem['attrs']['Target'].replace('../', 'ppt/')
300
+ 'target': relTarget
284
301
  }
285
302
  }
286
303
  }
@@ -558,6 +575,19 @@ async function processNodesInSlide(nodeKey, nodeValue, warpObj, source, groupHie
558
575
  default:
559
576
  }
560
577
 
578
+ if (json && typeof json === 'object' && !json.id) {
579
+ const id = getTextByPathList(nodeValue, ['p:nvSpPr', 'p:cNvPr', 'attrs', 'id']) ||
580
+ getTextByPathList(nodeValue, ['p:nvPicPr', 'p:cNvPr', 'attrs', 'id']) ||
581
+ getTextByPathList(nodeValue, ['p:nvCxnSpPr', 'p:cNvPr', 'attrs', 'id']) ||
582
+ getTextByPathList(nodeValue, ['p:nvGrpSpPr', 'p:cNvPr', 'attrs', 'id']) ||
583
+ getTextByPathList(nodeValue, ['p:nvGraphicFramePr', 'p:cNvPr', 'attrs', 'id']) ||
584
+ getTextByPathList(nodeValue, ['mc:Choice', 'p:sp', 'p:nvSpPr', 'p:cNvPr', 'attrs', 'id']) ||
585
+ getTextByPathList(nodeValue, ['mc:Fallback', 'p:sp', 'p:nvSpPr', 'p:cNvPr', 'attrs', 'id']) ||
586
+ getTextByPathList(nodeValue, ['mc:Fallback', 'p:nvGrpSpPr', 'p:cNvPr', 'attrs', 'id'])
587
+
588
+ json.id = id || ''
589
+ }
590
+
561
591
  return json
562
592
  }
563
593
 
@@ -795,7 +825,7 @@ async function genShape(node, slideLayoutSpNode, slideMasterSpNode, name, type,
795
825
  let content = ''
796
826
  if (node['p:txBody']) content = genTextBody(node['p:txBody'], node, slideLayoutSpNode, slideMasterSpNode, type, warpObj)
797
827
 
798
- const { borderColor, borderWidth, borderType, strokeDasharray } = getBorder(node, type, warpObj)
828
+ const { borderColor, borderWidth, borderType, strokeDasharray, headEnd, tailEnd } = getBorder(node, type, warpObj)
799
829
  const fill = await getShapeFill(node, warpObj, source, {
800
830
  groupHierarchy,
801
831
  slideLayoutSpNode,
@@ -808,6 +838,7 @@ async function genShape(node, slideLayoutSpNode, slideMasterSpNode, name, type,
808
838
 
809
839
  const vAlign = getVerticalAlign(node, slideLayoutSpNode, slideMasterSpNode, type)
810
840
  const isVertical = getTextByPathList(node, ['p:txBody', 'a:bodyPr', 'attrs', 'vert']) === 'eaVert'
841
+ const wrap = getTextByPathList(node, ['p:txBody', 'a:bodyPr', 'attrs', 'wrap']) !== 'none'
811
842
  const autoFit = getTextAutoFit(node, slideLayoutSpNode, slideMasterSpNode)
812
843
  const textInset = getTextInsets(node, slideLayoutSpNode, slideMasterSpNode)
813
844
 
@@ -826,6 +857,7 @@ async function genShape(node, slideLayoutSpNode, slideMasterSpNode, name, type,
826
857
  isFlipH,
827
858
  rotate,
828
859
  vAlign,
860
+ wrap,
829
861
  name,
830
862
  order,
831
863
  }
@@ -834,6 +866,8 @@ async function genShape(node, slideLayoutSpNode, slideMasterSpNode, name, type,
834
866
  if (autoFit) data.autoFit = autoFit
835
867
  if (link) data.link = link
836
868
  if (textInset) data.textInset = textInset
869
+ if (headEnd) data.headEnd = headEnd
870
+ if (tailEnd) data.tailEnd = tailEnd
837
871
 
838
872
  const isHasValidText = data.content && hasValidText(data.content)
839
873